Skip to content

Commit 38db31a

Browse files
committed
checkpoint
1 parent 2c446f1 commit 38db31a

File tree

3 files changed

+177
-48
lines changed

3 files changed

+177
-48
lines changed

src/components/date-range-picker/date-range-picker.spec.ts

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import IgcCalendarComponent from '../calendar/calendar.js';
44
import { getCalendarDOM, getDOMDate } from '../calendar/helpers.spec.js';
55
import { CalendarDay } from '../calendar/model.js';
66
import { defineComponents } from '../common/definitions/defineComponents.js';
7-
import { simulateClick } from '../common/utils.spec.js';
7+
import {
8+
isFocused,
9+
simulateClick,
10+
simulateKeyboard,
11+
} from '../common/utils.spec.js';
812
import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js';
913
import { DateTimeUtil } from '../date-time-input/date-util.js';
1014
import IgcInputComponent from '../input/input.js';
@@ -14,7 +18,7 @@ describe('Date range picker', () => {
1418
before(() => defineComponents(IgcDateRangePickerComponent));
1519

1620
let picker: IgcDateRangePickerComponent;
17-
let _dateTimeInput: IgcDateTimeInputComponent;
21+
let dateTimeInputs: Array<IgcDateTimeInputComponent>;
1822
let calendar: IgcCalendarComponent;
1923

2024
const today = CalendarDay.from(new Date());
@@ -24,9 +28,9 @@ describe('Date range picker', () => {
2428
picker = await fixture<IgcDateRangePickerComponent>(
2529
html`<igc-date-range-picker></igc-date-range-picker>`
2630
);
27-
_dateTimeInput = picker.renderRoot.querySelector(
28-
IgcDateTimeInputComponent.tagName
29-
)!;
31+
dateTimeInputs = Array.from(
32+
picker.renderRoot.querySelectorAll(IgcDateTimeInputComponent.tagName)
33+
);
3034

3135
calendar = picker.renderRoot.querySelector(IgcCalendarComponent.tagName)!;
3236
});
@@ -146,6 +150,19 @@ describe('Date range picker', () => {
146150
});
147151
});
148152
describe('Properties', () => {
153+
it('should set value through attribute correctly in case the date values are valid ISO 8601 strings', async () => {
154+
const expectedValue = [today.native, tomorrow.native];
155+
const attributeValue = `${today.native.toISOString()}, ${tomorrow.native.toISOString()}`;
156+
picker = await fixture<IgcDateRangePickerComponent>(
157+
html`<igc-date-range-picker
158+
value="${attributeValue}"
159+
></igc-date-range-picker>`
160+
);
161+
await elementUpdated(picker);
162+
163+
checkSelectedRange(picker, expectedValue);
164+
});
165+
149166
it('should keep the calendar selection and input values on changing the mode', async () => {
150167
const expectedValue = [today.native, tomorrow.native];
151168
picker = await fixture<IgcDateRangePickerComponent>(
@@ -178,6 +195,149 @@ describe('Date range picker', () => {
178195
await elementUpdated(picker);
179196
checkSelectedRange(picker, expectedValue);
180197
});
198+
199+
it('should not close calendar on clicking outside of it when keepOpenOnOutsideClick is true', async () => {
200+
expect(picker.open).to.equal(false);
201+
picker.keepOpenOnOutsideClick = true;
202+
await elementUpdated(picker);
203+
204+
await picker.show();
205+
206+
expect(picker.open).to.equal(true);
207+
208+
simulateClick(document.body);
209+
await elementUpdated(picker);
210+
211+
expect(picker.open).to.equal(true);
212+
213+
await picker.hide();
214+
215+
picker.mode = 'dialog';
216+
picker.keepOpenOnOutsideClick = true;
217+
await elementUpdated(picker);
218+
219+
await picker.show();
220+
221+
expect(picker.open).to.equal(true);
222+
simulateClick(document.body);
223+
await elementUpdated(picker);
224+
225+
expect(picker.open).to.equal(true);
226+
});
227+
228+
it('should close calendar on clicking outside of it when keepOpenOnOutsideClick is false (default)', async () => {
229+
expect(picker.open).to.equal(false);
230+
await elementUpdated(picker);
231+
232+
await picker.show();
233+
234+
expect(picker.open).to.equal(true);
235+
236+
simulateClick(document.body);
237+
await elementUpdated(picker);
238+
239+
expect(picker.open).to.equal(false);
240+
241+
picker.mode = 'dialog';
242+
await elementUpdated(picker);
243+
244+
await picker.show();
245+
246+
expect(picker.open).to.equal(true);
247+
simulateClick(document.body);
248+
await elementUpdated(picker);
249+
250+
expect(picker.open).to.equal(false);
251+
});
252+
253+
it('should keep the picker open when keepOpenOnSelect is enabled and a selection is made in the calendar picker', async () => {
254+
const eventSpy = spy(picker, 'emitEvent');
255+
picker.keepOpenOnSelect = true;
256+
await elementUpdated(picker);
257+
258+
await picker.show();
259+
260+
await selectDates(today, tomorrow, calendar);
261+
262+
expect(eventSpy).calledWith('igcChange');
263+
checkSelectedRange(picker, [today.native, tomorrow.native]);
264+
expect(picker.open).to.equal(true);
265+
266+
await picker.hide();
267+
picker.mode = 'dialog';
268+
await elementUpdated(picker);
269+
270+
expect(picker.open).to.equal(false);
271+
272+
await picker.show();
273+
await selectDates(today.add('day', 2), tomorrow.add('day', 2), calendar);
274+
275+
checkSelectedRange(picker, [
276+
today.add('day', 2).native,
277+
tomorrow.add('day', 2).native,
278+
]);
279+
expect(picker.open).to.equal(true);
280+
});
281+
282+
it('should not modify value through selection or typing when readOnly is true', async () => {
283+
const eventSpy = spy(picker, 'emitEvent');
284+
picker.readOnly = true;
285+
await elementUpdated(picker);
286+
expect(picker.value).to.deep.equal([null, null]); //TODO: refactor
287+
288+
await picker.show();
289+
await selectDates(today, tomorrow, calendar);
290+
291+
expect(picker.value).to.deep.equal([null, null]);
292+
expect(calendar.values).to.deep.equal([]);
293+
expect(eventSpy).not.to.be.called;
294+
295+
await picker.hide();
296+
297+
dateTimeInputs[0].focus();
298+
simulateKeyboard(dateTimeInputs[0], 'ArrowDown');
299+
await elementUpdated(picker);
300+
301+
expect(isFocused(dateTimeInputs[0])).to.be.true;
302+
expect(dateTimeInputs[0].value).to.equal(null);
303+
expect(picker.value).to.deep.equal([null, null]);
304+
expect(calendar.values).to.deep.equal([]);
305+
expect(eventSpy).not.to.be.called;
306+
307+
picker.singleInput = true;
308+
await elementUpdated(picker);
309+
310+
await picker.show();
311+
312+
calendar = picker.renderRoot.querySelector(IgcCalendarComponent.tagName)!;
313+
await selectDates(today, tomorrow, calendar);
314+
315+
expect(picker.value).to.deep.equal([null, null]);
316+
expect(calendar.values).to.deep.equal([]);
317+
expect(eventSpy).not.to.be.called;
318+
});
319+
320+
it('should modify value only through calendar selection and not input when nonEditable is true (two inputs)', async () => {
321+
const eventSpy = spy(picker, 'emitEvent');
322+
picker.nonEditable = true;
323+
await elementUpdated(picker);
324+
325+
dateTimeInputs[0].focus();
326+
simulateKeyboard(dateTimeInputs[0], 'ArrowDown');
327+
await elementUpdated(picker);
328+
329+
expect(isFocused(dateTimeInputs[0])).to.be.true;
330+
expect(dateTimeInputs[0].value).to.equal(null);
331+
expect(picker.value).to.deep.equal([null, null]);
332+
expect(calendar.values).to.deep.equal([]);
333+
expect(eventSpy).not.to.be.called;
334+
335+
await picker.show();
336+
await selectDates(today, tomorrow, calendar);
337+
338+
checkSelectedRange(picker, [today.native, tomorrow.native]);
339+
expect(eventSpy).calledWith('igcChange');
340+
});
181341
});
182342
describe('Interactions', () => {
183343
describe('Selection via the calendar', () => {

src/components/date-range-picker/date-range-picker.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,12 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
172172
}
173173

174174
@property({ converter: convertToDates })
175-
public set value(value: (Date | string | null)[] | null | undefined) {
176-
const converted = convertToDates(value?.map((d) => d ?? ''));
175+
public set value(
176+
value: (Date | null | string)[] | string | null | undefined
177+
) {
178+
const converted = convertToDates(
179+
typeof value === 'string' ? value : value?.map((d) => d ?? '')
180+
);
177181
this._startDate = converted?.[0] ?? null;
178182
this._endDate = converted?.[1] ?? null;
179183
this.setCalendarRangeValues();

stories/date-range-picker.stories.ts

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
IgcDateRangePickerComponent,
99
defineComponents,
1010
} from 'igniteui-webcomponents';
11+
import { CalendarDay } from '../src/components/calendar/model.js';
1112
import {
1213
disableStoryControls,
1314
formControls,
@@ -349,53 +350,16 @@ function selectToday() {
349350
picker.hide();
350351
}
351352

353+
const today = CalendarDay.from(new Date());
354+
const tomorrow = today.add('day', 1);
352355
export const Default: Story = {
353356
args: {
354357
open: false,
355358
},
356359
render: (args) => html`
357360
<igc-date-range-picker
358361
id="picker"
359-
.displayFormat=${args.displayFormat}
360-
.inputFormat=${args.inputFormat}
361-
.locale=${args.locale}
362-
.prompt=${args.prompt}
363-
.weekStart=${args.weekStart}
364-
.hideHeader=${args.hideHeader}
365-
.headerOrientation=${args.headerOrientation}
366-
.nonEditable=${args.nonEditable}
367-
.orientation=${args.orientation}
368-
.outlined=${args.outlined}
369-
.usePredefinedRanges=${args.usePredefinedRanges}
370-
.mode=${args.mode}
371-
.min=${new Date(args.min)}
372-
.max=${new Date(args.max)}
373-
.activeDate=${args.activeDate}
374-
?disabled=${args.disabled}
375-
.singleInput=${args.singleInput}
376-
?invalid=${args.invalid}
377-
?readonly=${args.readOnly}
378-
?required=${args.required}
379-
?open=${args.open}
380-
?show-week-numbers=${args.showWeekNumbers}
381-
?hide-outside-days=${args.hideOutsideDays}
382-
?keep-open-on-outside-click=${args.keepOpenOnOutsideClick}
383-
?keep-open-on-select=${args.keepOpenOnSelect}
384-
>
385-
</igc-date-range-picker>
386-
`,
387-
};
388-
389-
export const SingleInput: Story = {
390-
args: {
391-
open: false,
392-
singleInput: true,
393-
},
394-
render: (args) => html`
395-
<igc-date-range-picker
396-
id="picker"
397-
.label=${args.label}
398-
.placeholder=${args.placeholder}
362+
.value=${[today.native, tomorrow.native]}
399363
.displayFormat=${args.displayFormat}
400364
.inputFormat=${args.inputFormat}
401365
.locale=${args.locale}
@@ -533,6 +497,7 @@ export const Slots: Story = {
533497
</igc-date-range-picker>`,
534498
};
535499

500+
const ISOdatesString = '2025-03-01T00:00:00.000Z, 2025-03-02T00:00:00.000Z';
536501
export const FormTwoInputs: Story = {
537502
argTypes: disableStoryControls(metadata),
538503
render: () => html`
@@ -544,7 +509,7 @@ export const FormTwoInputs: Story = {
544509
<h5>Initial value</h5>
545510
<igc-date-range-picker
546511
name="picker-initial"
547-
.value=${[new Date(2025, 2, 19), new Date(2025, 2, 20)]}
512+
value=${ISOdatesString}
548513
></igc-date-range-picker>
549514
550515
<h5>Readonly</h5>

0 commit comments

Comments
 (0)