Skip to content

Commit 91b2a1b

Browse files
jcfrancobenelan
andauthored
fix(input-date-picker): reset active date picker date after closing (#7219)
**Related Issue:** #6495 ## Summary This ensures the active date is reset properly after a `calcite-input-date-picker`'s date picker is closed (either by blurring or selecting a date). ## Notes * This adds an internal `reset` method that can be made public in the future if needed. * There was an existing `reset` method prior to these changes that seems to clear the active date when the date picker is blurred or when `Escape` is pressed. We should revisit this behavior since it was [added when the input and date picker were a single component](https://github.com/Esri/calcite-components/blob/v1.0.0-beta.22/src/components/calcite-date/calcite-date.tsx#L263-L270) and it does seem odd to have the date-picker reset in these scenarios. cc @macandcheese @SkyeSeitz @ashetland * Utility test methods to interact with internal components were added and tests were updated to leverage them * Slightly increases the duration factor in the tests to work around #6604 --------- Co-authored-by: Ben Elan <[email protected]>
1 parent 52351c4 commit 91b2a1b

File tree

6 files changed

+144
-111
lines changed

6 files changed

+144
-111
lines changed

packages/calcite-components/src/components/date-picker/date-picker.tsx

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom
197197
this.el.focus();
198198
}
199199

200+
/**
201+
* Resets active date state.
202+
* @internal
203+
*/
204+
@Method()
205+
async reset(): Promise<void> {
206+
this.resetActiveDates();
207+
this.mostRecentRangeValue = undefined;
208+
}
209+
200210
// --------------------------------------------------------------------------
201211
//
202212
// Lifecycle
@@ -275,7 +285,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom
275285
: this.maxAsDate
276286
: this.maxAsDate;
277287
return (
278-
<Host onBlur={this.reset} onKeyDown={this.keyDownHandler}>
288+
<Host onBlur={this.resetActiveDates} onKeyDown={this.keyDownHandler}>
279289
{this.renderCalendar(activeDate, maxDate, minDate, date, endDate)}
280290
</Host>
281291
);
@@ -319,7 +329,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom
319329

320330
@State() private localeData: DateLocaleData;
321331

322-
private mostRecentRangeValue?: Date;
332+
@State() private mostRecentRangeValue?: Date;
323333

324334
@State() startAsDate: Date;
325335

@@ -331,7 +341,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom
331341

332342
keyDownHandler = (event: KeyboardEvent): void => {
333343
if (event.key === "Escape") {
334-
this.reset();
344+
this.resetActiveDates();
335345
}
336346
};
337347

@@ -509,31 +519,18 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom
509519
);
510520
}
511521

512-
/**
513-
* Reset active date and close
514-
*/
515-
reset = (): void => {
522+
private resetActiveDates = (): void => {
516523
const { valueAsDate } = this;
517-
if (
518-
!Array.isArray(valueAsDate) &&
519-
valueAsDate &&
520-
valueAsDate?.getTime() !== this.activeDate?.getTime()
521-
) {
524+
525+
if (!Array.isArray(valueAsDate) && valueAsDate && valueAsDate !== this.activeDate) {
522526
this.activeDate = new Date(valueAsDate);
523527
}
528+
524529
if (Array.isArray(valueAsDate)) {
525-
if (
526-
valueAsDate[0] &&
527-
valueAsDate[0]?.getTime() !==
528-
(this.activeStartDate instanceof Date && this.activeStartDate?.getTime())
529-
) {
530+
if (valueAsDate[0] && valueAsDate[0] !== this.activeStartDate) {
530531
this.activeStartDate = new Date(valueAsDate[0]);
531532
}
532-
if (
533-
valueAsDate[1] &&
534-
valueAsDate[1]?.getTime() !==
535-
(this.activeStartDate instanceof Date && this.activeEndDate?.getTime())
536-
) {
533+
if (valueAsDate[1] && valueAsDate[1] !== this.activeEndDate) {
537534
this.activeEndDate = new Date(valueAsDate[1]);
538535
}
539536
}

packages/calcite-components/src/components/dropdown/dropdown.e2e.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ describe("calcite-dropdown", () => {
4343
});
4444

4545
describe("disabled", () => {
46-
disabled(simpleDropdownHTML, { focusTarget: "child" });
46+
disabled(simpleDropdownHTML, {
47+
focusTarget: {
48+
tab: "calcite-button",
49+
click: "calcite-dropdown-item"
50+
}
51+
});
4752
});
4853

4954
interface SelectedItemsAssertionOptions {

packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,64 @@ describe("calcite-input-date-picker", () => {
5757

5858
it.skip("supports t9n", () => t9n("calcite-input-date-picker"));
5959

60+
async function navigateMonth(page: E2EPage, direction: "previous" | "next"): Promise<void> {
61+
const linkIndex = direction === "previous" ? 0 : 1;
62+
63+
await page.evaluate(
64+
async (MONTH_HEADER_CSS, linkIndex: number): Promise<void> =>
65+
document
66+
.querySelector("calcite-input-date-picker")
67+
.shadowRoot.querySelector("calcite-date-picker")
68+
.shadowRoot.querySelector("calcite-date-picker-month-header")
69+
.shadowRoot.querySelectorAll<HTMLAnchorElement>(`.${MONTH_HEADER_CSS.chevron}`)
70+
[linkIndex].click(),
71+
MONTH_HEADER_CSS,
72+
linkIndex
73+
);
74+
await page.waitForChanges();
75+
}
76+
77+
async function selectDayInMonth(page: E2EPage, day: number): Promise<void> {
78+
const dayIndex = day - 1;
79+
80+
await page.evaluate(
81+
async (dayIndex: number) =>
82+
document
83+
.querySelector<HTMLCalciteInputDatePickerElement>("calcite-input-date-picker")
84+
.shadowRoot.querySelector<HTMLCalciteDatePickerElement>("calcite-date-picker")
85+
.shadowRoot.querySelector<HTMLCalciteDatePickerMonthElement>("calcite-date-picker-month")
86+
.shadowRoot.querySelectorAll<HTMLCalciteDatePickerDayElement>("calcite-date-picker-day[current-month]")
87+
[dayIndex].click(),
88+
dayIndex
89+
);
90+
await page.waitForChanges();
91+
}
92+
93+
async function getActiveMonth(page: E2EPage): Promise<string> {
94+
return page.evaluate(
95+
async (MONTH_HEADER_CSS) =>
96+
document
97+
.querySelector("calcite-input-date-picker")
98+
.shadowRoot.querySelector("calcite-date-picker")
99+
.shadowRoot.querySelector("calcite-date-picker-month-header")
100+
.shadowRoot.querySelector(`.${MONTH_HEADER_CSS.month}`).textContent,
101+
MONTH_HEADER_CSS
102+
);
103+
}
104+
105+
async function getDateInputValue(page: E2EPage, type: "start" | "end" = "start"): Promise<string> {
106+
const inputIndex = type === "start" ? 0 : 1;
107+
108+
return page.evaluate(
109+
async (inputIndex: number): Promise<string> =>
110+
document
111+
.querySelector("calcite-input-date-picker")
112+
.shadowRoot.querySelectorAll("calcite-input")
113+
[inputIndex].shadowRoot.querySelector("input").value,
114+
inputIndex
115+
);
116+
}
117+
60118
describe("event emitting when the value changes", () => {
61119
it("emits change event when value is committed for single date", async () => {
62120
const page = await newE2EPage();
@@ -201,14 +259,7 @@ describe("calcite-input-date-picker", () => {
201259
await page.waitForChanges();
202260

203261
expect(changeEvent).toHaveReceivedEventTimes(0);
204-
205-
const inputValue = await page.evaluate(() => {
206-
const inputDatePicker = document.querySelector("calcite-input-date-picker");
207-
const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input");
208-
const input = calciteInput.shadowRoot.querySelector("input");
209-
return input.value;
210-
});
211-
expect(inputValue).toBe("3/7/");
262+
expect(await getDateInputValue(page)).toBe("3/7/");
212263
});
213264
});
214265

@@ -294,38 +345,30 @@ describe("calcite-input-date-picker", () => {
294345
await page.setContent(
295346
`<calcite-input-date-picker lang="ar" numbering-system="arab"></calcite-input-date-picker>`
296347
);
297-
const getInputValue = async () =>
298-
await page.evaluate(
299-
() =>
300-
document
301-
.querySelector("calcite-input-date-picker")
302-
.shadowRoot.querySelector("calcite-input")
303-
.shadowRoot.querySelector("input").value
304-
);
305348

306349
await page.keyboard.press("Tab");
307350
await page.keyboard.type("1/");
308351
await page.waitForChanges();
309352

310-
expect(await getInputValue()).toBe("١‏/");
353+
expect(await getDateInputValue(page)).toBe("١‏/");
311354

312355
await page.keyboard.type("2");
313356
await page.waitForChanges();
314357

315358
// NOTE: This asserted value was copied from the received value in a test failure caused by
316359
// typing these same values into the test file using an Arabic input source on macOS.
317360
// Make sure to preserve this value when refactoring instead of typing these characters from scratch.
318-
expect(await getInputValue()).toBe("١‏‏/٢");
361+
expect(await getDateInputValue(page)).toBe("١‏‏/٢");
319362

320363
await page.keyboard.type("/");
321364
await page.waitForChanges();
322365

323-
expect(await getInputValue()).toBe("١‏‏‏/٢‏/");
366+
expect(await getDateInputValue(page)).toBe("١‏‏‏/٢‏/");
324367

325368
await page.keyboard.type("1234");
326369
await page.waitForChanges();
327370

328-
expect(await getInputValue()).toBe("١‏‏‏‏‏‏‏/٢‏‏‏‏‏/١٢٣٤");
371+
expect(await getDateInputValue(page)).toBe("١‏‏‏‏‏‏‏/٢‏‏‏‏‏/١٢٣٤");
329372
});
330373

331374
it("syncs lang changes to internal date-picker and input", async () => {
@@ -348,36 +391,16 @@ describe("calcite-input-date-picker", () => {
348391
);
349392
const inputDatePicker = await page.find("calcite-input-date-picker");
350393

351-
const getLocalizedMonth = async () =>
352-
await page.evaluate(
353-
async (MONTH_HEADER_CSS) =>
354-
document
355-
.querySelector("calcite-input-date-picker")
356-
.shadowRoot.querySelector("calcite-date-picker")
357-
.shadowRoot.querySelector("calcite-date-picker-month-header")
358-
.shadowRoot.querySelector(`.${MONTH_HEADER_CSS.month}`).textContent,
359-
MONTH_HEADER_CSS
360-
);
361-
362-
const getLocalizedInputValue = async () =>
363-
await page.evaluate(
364-
async () =>
365-
document
366-
.querySelector("calcite-input-date-picker")
367-
.shadowRoot.querySelector("calcite-input")
368-
.shadowRoot.querySelector("input").value
369-
);
370-
371-
expect(await getLocalizedMonth()).toEqual(langTranslations.months.wide[Number(month) - 1]);
372-
expect(await getLocalizedInputValue()).toBe(
394+
expect(await getActiveMonth(page)).toEqual(langTranslations.months.wide[Number(month) - 1]);
395+
expect(await getDateInputValue(page)).toBe(
373396
langTranslations.placeholder.replace("DD", day).replace("MM", month).replace("YYYY", year)
374397
);
375398

376399
inputDatePicker.setProperty("lang", newLang);
377400
await page.waitForChanges();
378401

379-
expect(await getLocalizedMonth()).toEqual(newLangTranslations.months.wide[Number(month) - 1]);
380-
expect(await getLocalizedInputValue()).toBe(
402+
expect(await getActiveMonth(page)).toEqual(newLangTranslations.months.wide[Number(month) - 1]);
403+
expect(await getDateInputValue(page)).toBe(
381404
newLangTranslations.placeholder.replace("DD", day).replace("MM", month).replace("YYYY", year)
382405
);
383406
});
@@ -391,16 +414,7 @@ describe("calcite-input-date-picker", () => {
391414
await inputDatePicker.click();
392415
await calciteInputDatePickerOpenEvent;
393416

394-
await page.evaluate(async () =>
395-
// select first day of month
396-
document
397-
.querySelector<HTMLCalciteInputDatePickerElement>("calcite-input-date-picker")
398-
.shadowRoot.querySelector<HTMLCalciteDatePickerElement>("calcite-date-picker")
399-
.shadowRoot.querySelector<HTMLCalciteDatePickerMonthElement>("calcite-date-picker-month")
400-
.shadowRoot.querySelector<HTMLCalciteDatePickerDayElement>("calcite-date-picker-day[current-month]")
401-
.click()
402-
);
403-
await page.waitForChanges();
417+
await selectDayInMonth(page, 1);
404418
await inputDatePicker.callMethod("blur");
405419

406420
expect(await inputDatePicker.getProperty("value")).toBe("2023-05-01");
@@ -415,14 +429,7 @@ describe("calcite-input-date-picker", () => {
415429
await inputDatePicker.click();
416430
await page.waitForChanges();
417431

418-
await page.evaluate(() => {
419-
const inputDatePicker = document.querySelector("calcite-input-date-picker");
420-
const datePicker = inputDatePicker.shadowRoot.querySelector("calcite-date-picker");
421-
const datePickerMonth = datePicker.shadowRoot.querySelector("calcite-date-picker-month");
422-
const datePickerDay = datePickerMonth.shadowRoot.querySelector("calcite-date-picker-day");
423-
424-
datePickerDay.click();
425-
});
432+
await selectDayInMonth(page, 1);
426433

427434
expect(await inputDatePicker.getProperty("value")).toBe("2023-01-01");
428435
});
@@ -567,15 +574,7 @@ describe("calcite-input-date-picker", () => {
567574
const expectedInputValue = "10/1/2022";
568575

569576
expect(await inputDatePickerEl.getProperty("value")).toEqual(expectedValue);
570-
571-
const inputValue = await page.evaluate(() => {
572-
const inputDatePicker = document.querySelector("calcite-input-date-picker");
573-
const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input");
574-
const input = calciteInput.shadowRoot.querySelector("input");
575-
return input.value;
576-
});
577-
578-
expect(inputValue).toEqual(expectedInputValue);
577+
expect(await getDateInputValue(page)).toEqual(expectedInputValue);
579578
});
580579

581580
it("should update this.value and both input values when valueAsDate is set for range", async () => {
@@ -597,22 +596,8 @@ describe("calcite-input-date-picker", () => {
597596
const expectedStartDateInputValue = "10/1/2022";
598597
const expectedEndDateInputValue = "10/2/2022";
599598

600-
const startDateInputValue = await page.evaluate(() => {
601-
const inputDatePicker = document.querySelector("calcite-input-date-picker");
602-
const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input");
603-
const input = calciteInput.shadowRoot.querySelector("input");
604-
return input.value;
605-
});
606-
607-
const endDateInputValue = await page.evaluate(() => {
608-
const inputDatePicker = document.querySelector("calcite-input-date-picker");
609-
const calciteInputs = inputDatePicker.shadowRoot.querySelectorAll("calcite-input");
610-
const input = calciteInputs[1].shadowRoot.querySelector("input");
611-
return input.value;
612-
});
613-
614-
expect(startDateInputValue).toEqual(expectedStartDateInputValue);
615-
expect(endDateInputValue).toEqual(expectedEndDateInputValue);
599+
expect(await getDateInputValue(page, "start")).toEqual(expectedStartDateInputValue);
600+
expect(await getDateInputValue(page, "end")).toEqual(expectedEndDateInputValue);
616601
});
617602

618603
it("should return endDate time as 23:59:999 when valueAsDate property is parsed", async () => {
@@ -827,4 +812,41 @@ describe("calcite-input-date-picker", () => {
827812
expect(await inputDatePickerEl.getProperty("value")).toEqual("");
828813
expect(await input.getProperty("value")).toEqual("");
829814
});
815+
816+
it("should sync its date-pickers when updated programmatically after a user modifies the range", async () => {
817+
const page = await newE2EPage();
818+
await page.setContent(html`<calcite-input-date-picker range></calcite-input-date-picker>`);
819+
await skipAnimations(page);
820+
821+
const inputDatePicker = await page.find("calcite-input-date-picker");
822+
inputDatePicker.setProperty("value", ["2023-02-01", "2023-02-28"]);
823+
await page.waitForChanges();
824+
825+
const [startDatePicker, endDatePicker] = await page.findAll("calcite-input-date-picker >>> calcite-input");
826+
827+
await startDatePicker.click();
828+
await page.waitForChanges();
829+
830+
await navigateMonth(page, "previous");
831+
await selectDayInMonth(page, 1);
832+
833+
await endDatePicker.click();
834+
await page.waitForChanges();
835+
836+
await navigateMonth(page, "previous");
837+
await selectDayInMonth(page, 31);
838+
839+
inputDatePicker.setProperty("value", ["2022-10-01", "2022-10-31"]);
840+
await page.waitForChanges();
841+
842+
await startDatePicker.click();
843+
await page.waitForChanges();
844+
845+
expect(await getActiveMonth(page)).toBe("October");
846+
847+
await endDatePicker.click();
848+
await page.waitForChanges();
849+
850+
expect(await getActiveMonth(page)).toBe("October");
851+
});
830852
});

packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ export class InputDatePicker
787787
deactivateFocusTrap(this);
788788
this.restoreInputFocus();
789789
this.focusOnOpen = false;
790+
this.datePickerEl.reset();
790791
}
791792

792793
setStartInput = (el: HTMLCalciteInputElement): void => {

0 commit comments

Comments
 (0)