Skip to content

Commit c0d1345

Browse files
authored
Merge branch 'master' into mtsvyatkova/feat-1379-tile-manager
2 parents 9e5cafb + 86c843e commit c0d1345

27 files changed

+516
-327
lines changed

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
node-version: ${{ matrix.node-version }}
2828
cache: 'npm'
2929
- run: npm ci
30-
- run: npx playwright install --with-deps chromium
30+
- run: npx playwright install --with-deps --only-shell chromium
3131
- run: npm run lint
3232
- run: npm run test
3333
- run: npm run check

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [Unreleased]
8+
### Changed
9+
- Calendar - allow passing a string value to the backing `value`, `values` and `activeDate` properties [#1467](https://github.com/IgniteUI/igniteui-webcomponents/issues/1467)
10+
- Date-time input - allow passing a string value to the backing `value`, `min` and `max` properties [#1467](https://github.com/IgniteUI/igniteui-webcomponents/issues/1467)
11+
- Date picker - allow passing a string value to the backing `value`, `min`, `max` and `activeDate` properties [#1467](https://github.com/IgniteUI/igniteui-webcomponents/issues/1467)
12+
713
## [5.1.2] - 2024-11-04
814
### Added
915
- Carousel component select method overload accepting index [#1457](https://github.com/IgniteUI/igniteui-webcomponents/issues/1457)

package-lock.json

Lines changed: 145 additions & 153 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@
6464
"@custom-elements-manifest/analyzer": "^0.10.3",
6565
"@igniteui/material-icons-extended": "^3.1.0",
6666
"@open-wc/testing": "^4.0.0",
67-
"@storybook/addon-a11y": "^8.4.2",
68-
"@storybook/addon-actions": "^8.4.2",
69-
"@storybook/addon-essentials": "^8.4.2",
70-
"@storybook/addon-links": "^8.4.2",
71-
"@storybook/web-components": "^8.4.2",
72-
"@storybook/web-components-vite": "^8.4.2",
67+
"@storybook/addon-a11y": "^8.4.4",
68+
"@storybook/addon-actions": "^8.4.4",
69+
"@storybook/addon-essentials": "^8.4.4",
70+
"@storybook/addon-links": "^8.4.4",
71+
"@storybook/web-components": "^8.4.4",
72+
"@storybook/web-components-vite": "^8.4.4",
7373
"@types/mocha": "^10.0.9",
7474
"@web/test-runner": "^0.19.0",
7575
"@web/test-runner-playwright": "^0.11.0",
@@ -80,22 +80,22 @@
8080
"custom-element-jet-brains-integration": "^1.6.2",
8181
"custom-element-vs-code-integration": "^1.4.1",
8282
"globby": "^14.0.2",
83-
"husky": "^9.1.6",
83+
"husky": "^9.1.7",
8484
"ig-typedoc-theme": "^5.0.4",
8585
"igniteui-theming": "^14.2.0",
8686
"keep-a-changelog": "^2.5.3",
8787
"lint-staged": "^15.2.10",
8888
"lit-analyzer": "^2.0.3",
8989
"madge": "^8.0.0",
9090
"node-watch": "^0.7.4",
91-
"playwright": "^1.48.2",
91+
"playwright": "^1.49.0",
9292
"postcss": "^8.4.49",
9393
"prettier": "^3.3.3",
9494
"rimraf": "^5.0.10",
9595
"sass": "^1.78.0",
9696
"sass-embedded": "^1.78.0",
9797
"sinon": "^19.0.2",
98-
"storybook": "^8.4.2",
98+
"storybook": "^8.4.4",
9999
"stylelint": "^16.10.0",
100100
"stylelint-config-standard-scss": "^13.1.0",
101101
"stylelint-prettier": "^5.0.2",

src/components/calendar/base.ts

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ import { property, state } from 'lit/decorators.js';
44
import { blazorDeepImport } from '../common/decorators/blazorDeepImport.js';
55
import { blazorIndirectRender } from '../common/decorators/blazorIndirectRender.js';
66
import { watch } from '../common/decorators/watch.js';
7-
import {
8-
dateFromISOString,
9-
datesFromISOStrings,
10-
getWeekDayNumber,
11-
} from './helpers.js';
7+
import { first } from '../common/util.js';
8+
import { convertToDate, convertToDates, getWeekDayNumber } from './helpers.js';
129
import { CalendarDay } from './model.js';
1310
import type { DateRangeDescriptor, WeekDays } from './types.js';
1411

@@ -18,7 +15,7 @@ export class IgcCalendarBaseComponent extends LitElement {
1815
private _initialActiveDateSet = false;
1916

2017
protected get _hasValues() {
21-
return this._values.length > 0;
18+
return this._values && this._values.length > 0;
2219
}
2320

2421
protected get _isSingle() {
@@ -43,7 +40,7 @@ export class IgcCalendarBaseComponent extends LitElement {
4340
protected _activeDate = CalendarDay.today;
4441

4542
@state()
46-
protected _value?: CalendarDay;
43+
protected _value: CalendarDay | null = null;
4744

4845
@state()
4946
protected _values: CalendarDay[] = [];
@@ -54,24 +51,21 @@ export class IgcCalendarBaseComponent extends LitElement {
5451
@state()
5552
protected _disabledDates: DateRangeDescriptor[] = [];
5653

57-
public get value(): Date | undefined {
58-
return this._value ? this._value.native : undefined;
59-
}
60-
6154
/* blazorSuppress */
6255
/**
6356
* The current value of the calendar.
6457
* Used when selection is set to single
6558
*
6659
* @attr value
6760
*/
68-
@property({ converter: dateFromISOString })
69-
public set value(value) {
70-
this._value = value ? CalendarDay.from(value) : undefined;
61+
@property({ converter: convertToDate })
62+
public set value(value: Date | string | null | undefined) {
63+
const converted = convertToDate(value);
64+
this._value = converted ? CalendarDay.from(converted) : null;
7165
}
7266

73-
public get values(): Date[] {
74-
return this._values ? this._values.map((v) => v.native) : [];
67+
public get value(): Date | null {
68+
return this._value ? this._value.native : null;
7569
}
7670

7771
/* blazorSuppress */
@@ -81,21 +75,29 @@ export class IgcCalendarBaseComponent extends LitElement {
8175
*
8276
* @attr values
8377
*/
84-
@property({ converter: datesFromISOStrings })
85-
public set values(values) {
86-
this._values = values ? values.map((v) => CalendarDay.from(v)) : [];
78+
@property({ converter: convertToDates })
79+
public set values(values: (Date | string)[] | string | null | undefined) {
80+
const converted = convertToDates(values);
81+
this._values = converted ? converted.map((v) => CalendarDay.from(v)) : [];
8782
}
8883

89-
public get activeDate(): Date {
90-
return this._activeDate.native;
84+
public get values(): Date[] {
85+
return this._values ? this._values.map((v) => v.native) : [];
9186
}
9287

9388
/* blazorSuppress */
9489
/** Get/Set the date which is shown in view and is highlighted. By default it is the current date. */
95-
@property({ attribute: 'active-date', converter: dateFromISOString })
96-
public set activeDate(value) {
90+
@property({ attribute: 'active-date', converter: convertToDate })
91+
public set activeDate(value: Date | string | null | undefined) {
9792
this._initialActiveDateSet = true;
98-
this._activeDate = value ? CalendarDay.from(value) : CalendarDay.today;
93+
const converted = convertToDate(value);
94+
this._activeDate = converted
95+
? CalendarDay.from(converted)
96+
: CalendarDay.today;
97+
}
98+
99+
public get activeDate(): Date {
100+
return this._activeDate.native;
99101
}
100102

101103
/**
@@ -154,7 +156,7 @@ export class IgcCalendarBaseComponent extends LitElement {
154156
@watch('selection', { waitUntilFirstUpdate: true })
155157
protected selectionChanged() {
156158
this._rangePreviewDate = undefined;
157-
this._value = undefined;
159+
this._value = null;
158160
this._values = [];
159161
}
160162

@@ -166,7 +168,7 @@ export class IgcCalendarBaseComponent extends LitElement {
166168
if (this._isSingle) {
167169
this.activeDate = this.value ?? this.activeDate;
168170
} else {
169-
this.activeDate = this.values[0] ?? this.activeDate;
171+
this.activeDate = first(this.values) ?? this.activeDate;
170172
}
171173
}
172174
}

src/components/calendar/calendar.interaction.spec.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ describe('Calendar interactions', () => {
4646
expect(date.equalTo(calendar.value!)).to.be.true;
4747
});
4848

49+
it('setting `value` - string property binding', async () => {
50+
const date = new CalendarDay({ year: 2022, month: 0, date: 19 });
51+
calendar.value = date.native.toISOString();
52+
53+
expect(date.equalTo(calendar.value!)).to.be.true;
54+
55+
// Invalid date
56+
for (const each of [new Date('s'), '', null, undefined]) {
57+
calendar.value = each;
58+
expect(calendar.value).to.be.null;
59+
}
60+
});
61+
4962
it('setting `values` attribute', async () => {
5063
const date_1 = new CalendarDay({ year: 2022, month: 0, date: 19 });
5164
const date_2 = date_1.set({ date: 22 });
@@ -61,6 +74,57 @@ describe('Calendar interactions', () => {
6174
expect(date_2.equalTo(last(calendar.values))).to.be.true;
6275
});
6376

77+
it('setting `values` - string property binding', async () => {
78+
const date_1 = new CalendarDay({ year: 2022, month: 0, date: 19 });
79+
const date_2 = date_1.set({ date: 22 });
80+
81+
const date_1_str = date_1.native.toISOString();
82+
const date_2_str = date_2.native.toISOString();
83+
84+
calendar.selection = 'multiple';
85+
calendar.values = `${date_1_str}, ${date_2_str}`;
86+
87+
expect(calendar.values).lengthOf(2);
88+
expect(date_1.equalTo(first(calendar.values))).to.be.true;
89+
expect(date_2.equalTo(last(calendar.values))).to.be.true;
90+
91+
// Valid date combinations
92+
const validDates = [
93+
[date_1_str, date_2_str],
94+
[date_1.native, date_2.native],
95+
[date_1_str, date_2.native],
96+
];
97+
98+
for (const each of validDates) {
99+
calendar.values = each;
100+
expect(calendar.values).lengthOf(2);
101+
expect(date_1.equalTo(first(calendar.values))).to.be.true;
102+
expect(date_2.equalTo(last(calendar.values))).to.be.true;
103+
}
104+
105+
// Mixed date combinations
106+
calendar.values = [date_1.native, new Date(), new Date('s'), date_1_str];
107+
expect(calendar.values).lengthOf(3);
108+
109+
calendar.values = ['invalid', date_1_str, date_2_str, date_2.native];
110+
expect(calendar.values).lengthOf(3);
111+
112+
// Invalid date combinations
113+
const invalidDates = [
114+
'',
115+
null,
116+
undefined,
117+
[new Date('s'), 'abc'],
118+
'abcde, abcde',
119+
['a', 'b', 'c', new Date('invalid')],
120+
];
121+
122+
for (const each of invalidDates) {
123+
calendar.values = each;
124+
expect(calendar.values).is.empty;
125+
}
126+
});
127+
64128
it('clicking previous/next buttons in days view', async () => {
65129
const { previous, next } = getCalendarDOM(calendar).navigation;
66130

src/components/calendar/calendar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ export default class IgcCalendarComponent extends EventEmitterMixin<
700700
}
701701

702702
this.emitEvent('igcChange', {
703-
detail: this._isSingle ? this.value : this.values,
703+
detail: this._isSingle ? (this.value as Date) : this.values,
704704
});
705705
}
706706

src/components/calendar/helpers.ts

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
asNumber,
33
findElementFromEventPath,
44
first,
5+
isString,
56
last,
67
modulo,
78
} from '../common/util.js';
@@ -36,23 +37,79 @@ const DaysMap = {
3637

3738
/* Converter functions */
3839

39-
export function dateFromISOString(value: string | null) {
40-
return value ? new Date(value) : null;
40+
export function isValidDate(date: Date) {
41+
return Number.isNaN(date.valueOf()) ? null : date;
4142
}
4243

43-
export function datesFromISOStrings(value: string | null) {
44-
return value
45-
? value
46-
.split(',')
47-
.map((v) => v.trim())
48-
.filter((v) => v)
49-
.map((v) => new Date(v))
50-
: null;
44+
export function parseISODate(string: string) {
45+
if (/^\d{4}/.test(string)) {
46+
const time = !string.includes('T') ? 'T00:00:00' : '';
47+
return isValidDate(new Date(`${string}${time}`));
48+
}
49+
50+
if (/^\d{2}/.test(string)) {
51+
const date = first(new Date().toISOString().split('T'));
52+
return isValidDate(new Date(`${date}T${string}`));
53+
}
54+
55+
return null;
5156
}
5257

5358
/**
54-
* Returns the value of the selected/activated element (day/month/year) in the calendar view.
59+
* Converts the given value to a Date object.
60+
*
61+
* If the value is already a valid Date object, it is returned directly.
62+
* If the value is a string, it is parsed into a Date object.
63+
* If the value is null or undefined, null is returned.
64+
* If the parsing fails, null is returned.
65+
*/
66+
export function convertToDate(value?: Date | string | null): Date | null {
67+
if (!value) {
68+
return null;
69+
}
70+
71+
return isString(value) ? parseISODate(value) : isValidDate(value);
72+
}
73+
74+
/**
75+
* Converts a Date object to an ISO 8601 string.
76+
*
77+
* If the `value` is a `Date` object, it is converted to an ISO 8601 string.
78+
* If the `value` is null or undefined, null is returned.
79+
*/
80+
export function getDateFormValue(value: Date | null) {
81+
return value ? value.toISOString() : null;
82+
}
83+
84+
/**
85+
* Converts a comma-separated string of ISO 8601 dates or an array of Date objects | ISO 8601 strings into
86+
* an array of Date objects.
5587
*
88+
* If the `value` is null or undefined, null is returned.
89+
* If the `value` is an array of `Date` objects, a filtered array of valid `Date` objects is returned.
90+
* If the `value` is a string, it is split by commas and each part is parsed into a `Date` object.
91+
* If the parsing fails for any date, it is skipped.
92+
*/
93+
export function convertToDates(value?: (Date | string)[] | string | null) {
94+
if (!value) {
95+
return null;
96+
}
97+
98+
const values: Date[] = [];
99+
const iterator = isString(value) ? value.split(',') : value;
100+
101+
for (const each of iterator) {
102+
const date = convertToDate(isString(each) ? each.trim() : each);
103+
if (date) {
104+
values.push(date);
105+
}
106+
}
107+
108+
return values;
109+
}
110+
111+
/**
112+
* Returns the value of the selected/activated element (day/month/year) in the calendar view.
56113
*/
57114
export function getViewElement(event: Event) {
58115
const element = findElementFromEventPath<HTMLElement>('[data-value]', event);

src/components/checkbox/checkbox-base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export class IgcCheckboxBaseComponent extends FormAssociatedCheckboxRequiredMixi
8787

8888
protected override createRenderRoot() {
8989
const root = super.createRenderRoot();
90+
this.hideLabel = isEmpty(this.label);
9091

9192
root.addEventListener('slotchange', () => {
9293
this.hideLabel = isEmpty(this.label);

src/components/checkbox/checkbox.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent {
122122
.hidden=${this.hideLabel}
123123
part=${partNameMap({ label: true, checked })}
124124
id=${this.labelId}
125-
>
126-
<slot></slot>
125+
><slot></slot>
127126
</span>
128127
</label>
129128
${this.renderValidatorContainer()}

0 commit comments

Comments
 (0)