Skip to content

Commit 2c446f1

Browse files
committed
chore: add few tests, fix styles lint error
1 parent 84727f6 commit 2c446f1

File tree

3 files changed

+186
-110
lines changed

3 files changed

+186
-110
lines changed

src/components/date-range-picker/date-range-picker.base.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
display: flex;
33
flex-direction: column;
44
}
5+
56
[part='inputs'] {
67
display: flex;
78
flex-direction: row;
89
align-items: end;
910
}
11+
1012
[part='separator'] {
1113
margin: 0 20px;
1214
}
15+
1316
[part='predefined-ranges'] {
1417
display: flex;
1518
flex-direction: row;

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

Lines changed: 177 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { CalendarDay } from '../calendar/model.js';
66
import { defineComponents } from '../common/definitions/defineComponents.js';
77
import { simulateClick } from '../common/utils.spec.js';
88
import IgcDateTimeInputComponent from '../date-time-input/date-time-input.js';
9+
import { DateTimeUtil } from '../date-time-input/date-util.js';
10+
import IgcInputComponent from '../input/input.js';
911
import IgcDateRangePickerComponent from './date-range-picker.js';
1012

1113
describe('Date range picker', () => {
@@ -15,6 +17,9 @@ describe('Date range picker', () => {
1517
let _dateTimeInput: IgcDateTimeInputComponent;
1618
let calendar: IgcCalendarComponent;
1719

20+
const today = CalendarDay.from(new Date());
21+
const tomorrow = today.add('day', 1);
22+
1823
beforeEach(async () => {
1924
picker = await fixture<IgcDateRangePickerComponent>(
2025
html`<igc-date-range-picker></igc-date-range-picker>`
@@ -82,18 +87,14 @@ describe('Date range picker', () => {
8287
});
8388

8489
it('should be successfully initialized with value', async () => {
85-
const expectedValue = [new Date(2024, 2, 19), new Date(2025, 2, 20)];
90+
const expectedValue = [today.native, tomorrow.native];
8691
picker = await fixture<IgcDateRangePickerComponent>(
8792
html`<igc-date-range-picker
8893
.value="${expectedValue}"
8994
></igc-date-range-picker>`
9095
);
91-
_dateTimeInput = picker.renderRoot.querySelector(
92-
IgcDateTimeInputComponent.tagName
93-
)!;
9496

95-
expect(picker.value).not.to.be.null;
96-
expect(picker.value).to.deep.equal(expectedValue);
97+
checkSelectedRange(picker, expectedValue);
9798
});
9899

99100
it('should be successfully initialized in open state in dropdown mode', async () => {
@@ -125,104 +126,155 @@ describe('Date range picker', () => {
125126
expect(calendar).not.to.be.undefined;
126127
expect(calendar.parentElement).to.equal(dialog);
127128
});
128-
});
129-
describe('Selection via the calendar', () => {
130-
const today = CalendarDay.from(new Date());
131-
const tomorrow = today.add('day', 1);
132-
133-
it('should select a single date in dropdown mode and emit igcChange', async () => {
134-
const eventSpy = spy(picker, 'emitEvent');
135-
picker.open = true;
136-
await elementUpdated(picker);
137-
138-
await selectDates(today, null, calendar);
139129

140-
expect(eventSpy).calledWith('igcChange');
141-
expect(picker.value?.length).to.equal(2);
142-
expect(picker.value?.[0]).to.deep.equal(today.native);
143-
expect(picker.value?.[1]).to.deep.equal(today.native);
144-
145-
const popover = picker.renderRoot.querySelector('igc-popover');
146-
// when selecting a single date, the calendar won't close
147-
expect(popover?.hasAttribute('open')).to.equal(true);
148-
});
149-
150-
it('should select a range of dates in dropdown mode and emit igcChange', async () => {
151-
const eventSpy = spy(picker, 'emitEvent');
152-
153-
picker.open = true;
154-
await elementUpdated(picker);
155-
156-
await selectDates(today, tomorrow, calendar);
130+
it('should be initialized as single input', async () => {
131+
picker = await fixture<IgcDateRangePickerComponent>(
132+
html`<igc-date-range-picker single-input></igc-date-range-picker>`
133+
);
157134

158-
expect(eventSpy).calledWith('igcChange');
159-
expect(picker.value?.length).to.equal(2);
160-
expect(picker.value?.[0]).to.deep.equal(today.native);
161-
expect(picker.value?.[1]).to.deep.equal(tomorrow.native);
135+
expect(picker.singleInput).to.equal(true);
136+
await picker.show();
162137

163-
const popover = picker.renderRoot.querySelector('igc-popover');
164-
// with the second click, the calendar closes
165-
expect(popover?.hasAttribute('open')).to.equal(false);
138+
const input = picker.renderRoot.querySelectorAll(
139+
IgcInputComponent.tagName
140+
)!;
141+
expect(input).to.have.length(1);
142+
const dateTimeInputs = picker.renderRoot.querySelectorAll(
143+
IgcDateTimeInputComponent.tagName
144+
);
145+
expect(dateTimeInputs).to.have.length(0);
166146
});
147+
});
148+
describe('Properties', () => {
149+
it('should keep the calendar selection and input values on changing the mode', async () => {
150+
const expectedValue = [today.native, tomorrow.native];
151+
picker = await fixture<IgcDateRangePickerComponent>(
152+
html`<igc-date-range-picker
153+
.value="${expectedValue}"
154+
></igc-date-range-picker>`
155+
);
167156

168-
it('should select a range of dates in dialog mode and emit igcChange when done is clicked', async () => {
169-
const eventSpy = spy(picker, 'emitEvent');
157+
checkSelectedRange(picker, expectedValue);
170158

171159
picker.mode = 'dialog';
172160
await elementUpdated(picker);
173-
picker.open = true;
174-
await elementUpdated(picker);
175-
176-
await selectDates(today, tomorrow, calendar);
177-
178-
expect(eventSpy).not.to.be.calledWith('igcChange');
179-
let dialog = picker.renderRoot.querySelector('igc-dialog');
180-
expect(dialog?.hasAttribute('open')).to.equal(true);
181161

182-
const doneBtn = picker.shadowRoot!.querySelector(
183-
'igc-button[slot="footer"]:last-of-type'
184-
) as HTMLButtonElement;
185-
doneBtn?.click();
186-
await elementUpdated(picker);
187-
188-
expect(eventSpy).calledWith('igcChange');
189-
expect(picker.value?.length).to.equal(2);
190-
expect(picker.value?.[0]).to.deep.equal(today.native);
191-
expect(picker.value?.[1]).to.deep.equal(tomorrow.native);
192-
193-
dialog = picker.renderRoot.querySelector('igc-dialog');
194-
expect(dialog?.hasAttribute('open')).to.equal(false);
162+
checkSelectedRange(picker, expectedValue);
195163
});
196164

197-
it('should not emit igcChange when cancel is clicked and the value should be the initial value', async () => {
198-
const eventSpy = spy(picker, 'emitEvent');
165+
it('should keep the calendar selection and input values on switching to singleInput and back', async () => {
166+
const expectedValue = [today.native, tomorrow.native];
167+
picker = await fixture<IgcDateRangePickerComponent>(
168+
html`<igc-date-range-picker
169+
.value="${expectedValue}"
170+
></igc-date-range-picker>`
171+
);
172+
checkSelectedRange(picker, expectedValue);
199173

200-
picker.mode = 'dialog';
201-
const date1 = today.add('day', -2);
202-
const date2 = today.add('day', 2);
203-
picker.value = [date1.native, date2.native];
174+
picker.singleInput = true;
204175
await elementUpdated(picker);
205-
picker.open = true;
176+
checkSelectedRange(picker, expectedValue, true);
177+
picker.singleInput = false;
206178
await elementUpdated(picker);
207-
208-
await selectDates(date1, date2, calendar);
209-
210-
expect(eventSpy).not.to.be.calledWith('igcChange');
211-
let dialog = picker.renderRoot.querySelector('igc-dialog');
212-
expect(dialog?.hasAttribute('open')).to.equal(true);
213-
214-
const cancelBtn = picker.shadowRoot!.querySelector(
215-
'igc-button[slot="footer"]'
216-
) as HTMLButtonElement;
217-
cancelBtn?.click();
218-
await elementUpdated(picker);
219-
220-
expect(eventSpy).not.to.be.calledWith('igcChange');
221-
expect(picker.value?.length).to.equal(2);
222-
expect(picker.value?.[0]).to.deep.equal(date1.native);
223-
expect(picker.value?.[1]).to.deep.equal(date2.native);
224-
dialog = picker.renderRoot.querySelector('igc-dialog');
225-
expect(dialog?.hasAttribute('open')).to.equal(false);
179+
checkSelectedRange(picker, expectedValue);
180+
});
181+
});
182+
describe('Interactions', () => {
183+
describe('Selection via the calendar', () => {
184+
it('should select a single date in dropdown mode and emit igcChange', async () => {
185+
const eventSpy = spy(picker, 'emitEvent');
186+
picker.open = true;
187+
await elementUpdated(picker);
188+
189+
await selectDates(today, null, calendar);
190+
191+
expect(eventSpy).calledWith('igcChange');
192+
expect(picker.value?.length).to.equal(2);
193+
expect(picker.value?.[0]).to.deep.equal(today.native);
194+
expect(picker.value?.[1]).to.deep.equal(today.native);
195+
196+
const popover = picker.renderRoot.querySelector('igc-popover');
197+
// when selecting a single date, the calendar won't close
198+
expect(popover?.hasAttribute('open')).to.equal(true);
199+
});
200+
201+
it('should select a range of dates in dropdown mode and emit igcChange', async () => {
202+
const eventSpy = spy(picker, 'emitEvent');
203+
204+
picker.open = true;
205+
await elementUpdated(picker);
206+
207+
await selectDates(today, tomorrow, calendar);
208+
209+
expect(eventSpy).calledWith('igcChange');
210+
expect(picker.value?.length).to.equal(2);
211+
expect(picker.value?.[0]).to.deep.equal(today.native);
212+
expect(picker.value?.[1]).to.deep.equal(tomorrow.native);
213+
214+
const popover = picker.renderRoot.querySelector('igc-popover');
215+
// with the second click, the calendar closes
216+
expect(popover?.hasAttribute('open')).to.equal(false);
217+
});
218+
219+
it('should select a range of dates in dialog mode and emit igcChange when done is clicked', async () => {
220+
const eventSpy = spy(picker, 'emitEvent');
221+
222+
picker.mode = 'dialog';
223+
await elementUpdated(picker);
224+
picker.open = true;
225+
await elementUpdated(picker);
226+
227+
await selectDates(today, tomorrow, calendar);
228+
229+
expect(eventSpy).not.to.be.calledWith('igcChange');
230+
let dialog = picker.renderRoot.querySelector('igc-dialog');
231+
expect(dialog?.hasAttribute('open')).to.equal(true);
232+
233+
const doneBtn = picker.shadowRoot!.querySelector(
234+
'igc-button[slot="footer"]:last-of-type'
235+
) as HTMLButtonElement;
236+
doneBtn?.click();
237+
await elementUpdated(picker);
238+
239+
expect(eventSpy).calledWith('igcChange');
240+
expect(picker.value?.length).to.equal(2);
241+
expect(picker.value?.[0]).to.deep.equal(today.native);
242+
expect(picker.value?.[1]).to.deep.equal(tomorrow.native);
243+
244+
dialog = picker.renderRoot.querySelector('igc-dialog');
245+
expect(dialog?.hasAttribute('open')).to.equal(false);
246+
});
247+
248+
it('should not emit igcChange when cancel is clicked and the value should be the initial value', async () => {
249+
const eventSpy = spy(picker, 'emitEvent');
250+
251+
picker.mode = 'dialog';
252+
const date1 = today.add('day', -2);
253+
const date2 = today.add('day', 2);
254+
picker.value = [date1.native, date2.native];
255+
await elementUpdated(picker);
256+
picker.open = true;
257+
await elementUpdated(picker);
258+
259+
await selectDates(date1, date2, calendar);
260+
261+
expect(eventSpy).not.to.be.calledWith('igcChange');
262+
let dialog = picker.renderRoot.querySelector('igc-dialog');
263+
expect(dialog?.hasAttribute('open')).to.equal(true);
264+
265+
const cancelBtn = picker.shadowRoot!.querySelector(
266+
'igc-button[slot="footer"]'
267+
) as HTMLButtonElement;
268+
cancelBtn?.click();
269+
await elementUpdated(picker);
270+
271+
expect(eventSpy).not.to.be.calledWith('igcChange');
272+
expect(picker.value?.length).to.equal(2);
273+
expect(picker.value?.[0]).to.deep.equal(date1.native);
274+
expect(picker.value?.[1]).to.deep.equal(date2.native);
275+
dialog = picker.renderRoot.querySelector('igc-dialog');
276+
expect(dialog?.hasAttribute('open')).to.equal(false);
277+
});
226278
});
227279
});
228280
});
@@ -244,3 +296,38 @@ const selectDates = async (
244296
await elementUpdated(calendar);
245297
}
246298
};
299+
300+
const checkSelectedRange = (
301+
picker: IgcDateRangePickerComponent,
302+
expectedValue: (Date | null)[],
303+
singleInput = false
304+
) => {
305+
const calendar = picker.renderRoot.querySelector(
306+
IgcCalendarComponent.tagName
307+
)!;
308+
309+
expect(picker.value).not.to.be.null;
310+
expect(picker.value).to.deep.equal(expectedValue);
311+
312+
if (singleInput) {
313+
const input = picker.renderRoot.querySelector(IgcInputComponent.tagName)!;
314+
const start = DateTimeUtil.formatDate(
315+
expectedValue[0]!,
316+
picker.locale,
317+
picker.displayFormat || picker.inputFormat
318+
);
319+
const end = DateTimeUtil.formatDate(
320+
expectedValue[1]!,
321+
picker.locale,
322+
picker.displayFormat || picker.inputFormat
323+
);
324+
expect(input.value).to.equal(`${start} - ${end}`);
325+
} else {
326+
const inputs = picker.renderRoot.querySelectorAll(
327+
IgcDateTimeInputComponent.tagName
328+
);
329+
expect(inputs[0].value).to.deep.equal(expectedValue[0]);
330+
expect(inputs[1].value).to.deep.equal(expectedValue[1]);
331+
}
332+
expect(calendar.values).to.deep.equal(expectedValue);
333+
};

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

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -425,22 +425,6 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
425425
.set(escapeKey, this.onEscapeKey);
426426
}
427427

428-
protected override createRenderRoot() {
429-
const root = super.createRenderRoot();
430-
root.addEventListener('slotchange', () => {
431-
if (!this.singleInput) {
432-
this.setFormats();
433-
this.setKeepOpenOnSelectForDialog();
434-
if (this.value) {
435-
this.updateInputValues();
436-
}
437-
this.setDateConstraints();
438-
}
439-
this.requestUpdate();
440-
});
441-
return root;
442-
}
443-
444428
protected override firstUpdated() {
445429
this.setCalendarRangeValues();
446430
}
@@ -600,6 +584,7 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
600584
@watch('inputFormat')
601585
@watch('displayFormat')
602586
@watch('locale')
587+
@watch('singleInput')
603588
private updateMaskedRangeValue() {
604589
if (!this.singleInput) {
605590
return;
@@ -625,13 +610,14 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
625610
}
626611

627612
@watch('mode')
628-
private async setKeepOpenOnSelectForDialog() {
613+
private async modeChanged() {
629614
if (this.mode === 'dialog') {
630615
this.keepOpenOnSelect = true;
631616
}
617+
await this.setCalendarRangeValues();
632618
}
633619

634-
@watch('mode')
620+
@watch('singleInput')
635621
private async setCalendarRangeValues() {
636622
if (!this._calendar || !this._startDate || !this._endDate) {
637623
return;
@@ -941,12 +927,12 @@ export default class IgcDateRangePickerComponent extends FormAssociatedRequiredM
941927
private renderInputs(idStart: string, idEnd: string) {
942928
return html`
943929
<div part="inputs">
944-
${this.renderInput(idStart, 'start')}${this.renderPicker(idStart)}
930+
${this.renderInput(idStart, 'start')}
945931
<!-- TODO: localize separator string -->
946932
<span part="separator">to</span>
947933
${this.renderInput(idEnd, 'end')}
948934
</div>
949-
${this.renderHelperText()}
935+
${this.renderPicker(idStart)} ${this.renderHelperText()}
950936
`;
951937
}
952938

0 commit comments

Comments
 (0)