diff --git a/packages/calcite-components/src/components/date-picker/date-picker.tsx b/packages/calcite-components/src/components/date-picker/date-picker.tsx index a1715232fc3..e9c5fa310e4 100644 --- a/packages/calcite-components/src/components/date-picker/date-picker.tsx +++ b/packages/calcite-components/src/components/date-picker/date-picker.tsx @@ -197,6 +197,16 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom this.el.focus(); } + /** + * Resets active date state. + * @internal + */ + @Method() + async reset(): Promise { + this.resetActiveDates(); + this.mostRecentRangeValue = undefined; + } + // -------------------------------------------------------------------------- // // Lifecycle @@ -275,7 +285,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom : this.maxAsDate : this.maxAsDate; return ( - + {this.renderCalendar(activeDate, maxDate, minDate, date, endDate)} ); @@ -319,7 +329,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom @State() private localeData: DateLocaleData; - private mostRecentRangeValue?: Date; + @State() private mostRecentRangeValue?: Date; @State() startAsDate: Date; @@ -331,7 +341,7 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom keyDownHandler = (event: KeyboardEvent): void => { if (event.key === "Escape") { - this.reset(); + this.resetActiveDates(); } }; @@ -509,31 +519,18 @@ export class DatePicker implements LocalizedComponent, LoadableComponent, T9nCom ); } - /** - * Reset active date and close - */ - reset = (): void => { + private resetActiveDates = (): void => { const { valueAsDate } = this; - if ( - !Array.isArray(valueAsDate) && - valueAsDate && - valueAsDate?.getTime() !== this.activeDate?.getTime() - ) { + + if (!Array.isArray(valueAsDate) && valueAsDate && valueAsDate !== this.activeDate) { this.activeDate = new Date(valueAsDate); } + if (Array.isArray(valueAsDate)) { - if ( - valueAsDate[0] && - valueAsDate[0]?.getTime() !== - (this.activeStartDate instanceof Date && this.activeStartDate?.getTime()) - ) { + if (valueAsDate[0] && valueAsDate[0] !== this.activeStartDate) { this.activeStartDate = new Date(valueAsDate[0]); } - if ( - valueAsDate[1] && - valueAsDate[1]?.getTime() !== - (this.activeStartDate instanceof Date && this.activeEndDate?.getTime()) - ) { + if (valueAsDate[1] && valueAsDate[1] !== this.activeEndDate) { this.activeEndDate = new Date(valueAsDate[1]); } } diff --git a/packages/calcite-components/src/components/dropdown/dropdown.e2e.ts b/packages/calcite-components/src/components/dropdown/dropdown.e2e.ts index a0f123aa7e2..7c03710702a 100644 --- a/packages/calcite-components/src/components/dropdown/dropdown.e2e.ts +++ b/packages/calcite-components/src/components/dropdown/dropdown.e2e.ts @@ -43,7 +43,12 @@ describe("calcite-dropdown", () => { }); describe("disabled", () => { - disabled(simpleDropdownHTML, { focusTarget: "child" }); + disabled(simpleDropdownHTML, { + focusTarget: { + tab: "calcite-button", + click: "calcite-dropdown-item" + } + }); }); interface SelectedItemsAssertionOptions { diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts index a8df2a9f218..f018f3e4fa5 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts @@ -57,6 +57,64 @@ describe("calcite-input-date-picker", () => { it.skip("supports t9n", () => t9n("calcite-input-date-picker")); + async function navigateMonth(page: E2EPage, direction: "previous" | "next"): Promise { + const linkIndex = direction === "previous" ? 0 : 1; + + await page.evaluate( + async (MONTH_HEADER_CSS, linkIndex: number): Promise => + document + .querySelector("calcite-input-date-picker") + .shadowRoot.querySelector("calcite-date-picker") + .shadowRoot.querySelector("calcite-date-picker-month-header") + .shadowRoot.querySelectorAll(`.${MONTH_HEADER_CSS.chevron}`) + [linkIndex].click(), + MONTH_HEADER_CSS, + linkIndex + ); + await page.waitForChanges(); + } + + async function selectDayInMonth(page: E2EPage, day: number): Promise { + const dayIndex = day - 1; + + await page.evaluate( + async (dayIndex: number) => + document + .querySelector("calcite-input-date-picker") + .shadowRoot.querySelector("calcite-date-picker") + .shadowRoot.querySelector("calcite-date-picker-month") + .shadowRoot.querySelectorAll("calcite-date-picker-day[current-month]") + [dayIndex].click(), + dayIndex + ); + await page.waitForChanges(); + } + + async function getActiveMonth(page: E2EPage): Promise { + return page.evaluate( + async (MONTH_HEADER_CSS) => + document + .querySelector("calcite-input-date-picker") + .shadowRoot.querySelector("calcite-date-picker") + .shadowRoot.querySelector("calcite-date-picker-month-header") + .shadowRoot.querySelector(`.${MONTH_HEADER_CSS.month}`).textContent, + MONTH_HEADER_CSS + ); + } + + async function getDateInputValue(page: E2EPage, type: "start" | "end" = "start"): Promise { + const inputIndex = type === "start" ? 0 : 1; + + return page.evaluate( + async (inputIndex: number): Promise => + document + .querySelector("calcite-input-date-picker") + .shadowRoot.querySelectorAll("calcite-input") + [inputIndex].shadowRoot.querySelector("input").value, + inputIndex + ); + } + describe("event emitting when the value changes", () => { it("emits change event when value is committed for single date", async () => { const page = await newE2EPage(); @@ -201,14 +259,7 @@ describe("calcite-input-date-picker", () => { await page.waitForChanges(); expect(changeEvent).toHaveReceivedEventTimes(0); - - const inputValue = await page.evaluate(() => { - const inputDatePicker = document.querySelector("calcite-input-date-picker"); - const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input"); - const input = calciteInput.shadowRoot.querySelector("input"); - return input.value; - }); - expect(inputValue).toBe("3/7/"); + expect(await getDateInputValue(page)).toBe("3/7/"); }); }); @@ -294,20 +345,12 @@ describe("calcite-input-date-picker", () => { await page.setContent( `` ); - const getInputValue = async () => - await page.evaluate( - () => - document - .querySelector("calcite-input-date-picker") - .shadowRoot.querySelector("calcite-input") - .shadowRoot.querySelector("input").value - ); await page.keyboard.press("Tab"); await page.keyboard.type("1/"); await page.waitForChanges(); - expect(await getInputValue()).toBe("١‏/"); + expect(await getDateInputValue(page)).toBe("١‏/"); await page.keyboard.type("2"); await page.waitForChanges(); @@ -315,17 +358,17 @@ describe("calcite-input-date-picker", () => { // NOTE: This asserted value was copied from the received value in a test failure caused by // typing these same values into the test file using an Arabic input source on macOS. // Make sure to preserve this value when refactoring instead of typing these characters from scratch. - expect(await getInputValue()).toBe("١‏‏/٢"); + expect(await getDateInputValue(page)).toBe("١‏‏/٢"); await page.keyboard.type("/"); await page.waitForChanges(); - expect(await getInputValue()).toBe("١‏‏‏/٢‏/"); + expect(await getDateInputValue(page)).toBe("١‏‏‏/٢‏/"); await page.keyboard.type("1234"); await page.waitForChanges(); - expect(await getInputValue()).toBe("١‏‏‏‏‏‏‏/٢‏‏‏‏‏/١٢٣٤"); + expect(await getDateInputValue(page)).toBe("١‏‏‏‏‏‏‏/٢‏‏‏‏‏/١٢٣٤"); }); it("syncs lang changes to internal date-picker and input", async () => { @@ -348,36 +391,16 @@ describe("calcite-input-date-picker", () => { ); const inputDatePicker = await page.find("calcite-input-date-picker"); - const getLocalizedMonth = async () => - await page.evaluate( - async (MONTH_HEADER_CSS) => - document - .querySelector("calcite-input-date-picker") - .shadowRoot.querySelector("calcite-date-picker") - .shadowRoot.querySelector("calcite-date-picker-month-header") - .shadowRoot.querySelector(`.${MONTH_HEADER_CSS.month}`).textContent, - MONTH_HEADER_CSS - ); - - const getLocalizedInputValue = async () => - await page.evaluate( - async () => - document - .querySelector("calcite-input-date-picker") - .shadowRoot.querySelector("calcite-input") - .shadowRoot.querySelector("input").value - ); - - expect(await getLocalizedMonth()).toEqual(langTranslations.months.wide[Number(month) - 1]); - expect(await getLocalizedInputValue()).toBe( + expect(await getActiveMonth(page)).toEqual(langTranslations.months.wide[Number(month) - 1]); + expect(await getDateInputValue(page)).toBe( langTranslations.placeholder.replace("DD", day).replace("MM", month).replace("YYYY", year) ); inputDatePicker.setProperty("lang", newLang); await page.waitForChanges(); - expect(await getLocalizedMonth()).toEqual(newLangTranslations.months.wide[Number(month) - 1]); - expect(await getLocalizedInputValue()).toBe( + expect(await getActiveMonth(page)).toEqual(newLangTranslations.months.wide[Number(month) - 1]); + expect(await getDateInputValue(page)).toBe( newLangTranslations.placeholder.replace("DD", day).replace("MM", month).replace("YYYY", year) ); }); @@ -391,16 +414,7 @@ describe("calcite-input-date-picker", () => { await inputDatePicker.click(); await calciteInputDatePickerOpenEvent; - await page.evaluate(async () => - // select first day of month - document - .querySelector("calcite-input-date-picker") - .shadowRoot.querySelector("calcite-date-picker") - .shadowRoot.querySelector("calcite-date-picker-month") - .shadowRoot.querySelector("calcite-date-picker-day[current-month]") - .click() - ); - await page.waitForChanges(); + await selectDayInMonth(page, 1); await inputDatePicker.callMethod("blur"); expect(await inputDatePicker.getProperty("value")).toBe("2023-05-01"); @@ -415,14 +429,7 @@ describe("calcite-input-date-picker", () => { await inputDatePicker.click(); await page.waitForChanges(); - await page.evaluate(() => { - const inputDatePicker = document.querySelector("calcite-input-date-picker"); - const datePicker = inputDatePicker.shadowRoot.querySelector("calcite-date-picker"); - const datePickerMonth = datePicker.shadowRoot.querySelector("calcite-date-picker-month"); - const datePickerDay = datePickerMonth.shadowRoot.querySelector("calcite-date-picker-day"); - - datePickerDay.click(); - }); + await selectDayInMonth(page, 1); expect(await inputDatePicker.getProperty("value")).toBe("2023-01-01"); }); @@ -567,15 +574,7 @@ describe("calcite-input-date-picker", () => { const expectedInputValue = "10/1/2022"; expect(await inputDatePickerEl.getProperty("value")).toEqual(expectedValue); - - const inputValue = await page.evaluate(() => { - const inputDatePicker = document.querySelector("calcite-input-date-picker"); - const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input"); - const input = calciteInput.shadowRoot.querySelector("input"); - return input.value; - }); - - expect(inputValue).toEqual(expectedInputValue); + expect(await getDateInputValue(page)).toEqual(expectedInputValue); }); 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", () => { const expectedStartDateInputValue = "10/1/2022"; const expectedEndDateInputValue = "10/2/2022"; - const startDateInputValue = await page.evaluate(() => { - const inputDatePicker = document.querySelector("calcite-input-date-picker"); - const calciteInput = inputDatePicker.shadowRoot.querySelector("calcite-input"); - const input = calciteInput.shadowRoot.querySelector("input"); - return input.value; - }); - - const endDateInputValue = await page.evaluate(() => { - const inputDatePicker = document.querySelector("calcite-input-date-picker"); - const calciteInputs = inputDatePicker.shadowRoot.querySelectorAll("calcite-input"); - const input = calciteInputs[1].shadowRoot.querySelector("input"); - return input.value; - }); - - expect(startDateInputValue).toEqual(expectedStartDateInputValue); - expect(endDateInputValue).toEqual(expectedEndDateInputValue); + expect(await getDateInputValue(page, "start")).toEqual(expectedStartDateInputValue); + expect(await getDateInputValue(page, "end")).toEqual(expectedEndDateInputValue); }); it("should return endDate time as 23:59:999 when valueAsDate property is parsed", async () => { @@ -827,4 +812,41 @@ describe("calcite-input-date-picker", () => { expect(await inputDatePickerEl.getProperty("value")).toEqual(""); expect(await input.getProperty("value")).toEqual(""); }); + + it("should sync its date-pickers when updated programmatically after a user modifies the range", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + await skipAnimations(page); + + const inputDatePicker = await page.find("calcite-input-date-picker"); + inputDatePicker.setProperty("value", ["2023-02-01", "2023-02-28"]); + await page.waitForChanges(); + + const [startDatePicker, endDatePicker] = await page.findAll("calcite-input-date-picker >>> calcite-input"); + + await startDatePicker.click(); + await page.waitForChanges(); + + await navigateMonth(page, "previous"); + await selectDayInMonth(page, 1); + + await endDatePicker.click(); + await page.waitForChanges(); + + await navigateMonth(page, "previous"); + await selectDayInMonth(page, 31); + + inputDatePicker.setProperty("value", ["2022-10-01", "2022-10-31"]); + await page.waitForChanges(); + + await startDatePicker.click(); + await page.waitForChanges(); + + expect(await getActiveMonth(page)).toBe("October"); + + await endDatePicker.click(); + await page.waitForChanges(); + + expect(await getActiveMonth(page)).toBe("October"); + }); }); diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx index 9ee4ae2b328..7604d85408c 100644 --- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx +++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx @@ -787,6 +787,7 @@ export class InputDatePicker deactivateFocusTrap(this); this.restoreInputFocus(); this.focusOnOpen = false; + this.datePickerEl.reset(); } setStartInput = (el: HTMLCalciteInputElement): void => { diff --git a/packages/calcite-components/src/tests/commonTests.ts b/packages/calcite-components/src/tests/commonTests.ts index 4bc137f91a4..481d99fb731 100644 --- a/packages/calcite-components/src/tests/commonTests.ts +++ b/packages/calcite-components/src/tests/commonTests.ts @@ -1008,6 +1008,7 @@ export function disabled( if (options.focusTarget === "none") { await page.click(tag); + await page.waitForChanges(); await expectToBeFocused(page, "body"); assertOnMouseAndPointerEvents(eventSpies, (spy) => expect(spy).toHaveReceivedEventTimes(1)); @@ -1018,9 +1019,11 @@ export function disabled( expect(component.getAttribute("aria-disabled")).toBe("true"); await page.click(tag); + await page.waitForChanges(); await expectToBeFocused(page, "body"); await component.callMethod("click"); + await page.waitForChanges(); await expectToBeFocused(page, "body"); assertOnMouseAndPointerEvents(eventSpies, (spy) => { @@ -1051,9 +1054,11 @@ export function disabled( await expectToBeFocused(page, "body"); await page.mouse.click(shadowFocusableCenterX, shadowFocusableCenterY); + await page.waitForChanges(); await expectToBeFocused(page, clickFocusTarget); await component.callMethod("click"); + await page.waitForChanges(); await expectToBeFocused(page, clickFocusTarget); assertOnMouseAndPointerEvents(eventSpies, (spy) => { @@ -1104,6 +1109,7 @@ export function disabled( expect(component.getAttribute("aria-disabled")).toBe("true"); await page.click(tag); + await page.waitForChanges(); assertOnMouseAndPointerEvents(eventSpies, (spy) => { expect(spy).toHaveReceivedEventTimes(eventsExpectedToBubble.includes(spy.eventName) ? 1 : 0); diff --git a/packages/calcite-components/src/tests/utils.ts b/packages/calcite-components/src/tests/utils.ts index 76eddb391be..381090826be 100644 --- a/packages/calcite-components/src/tests/utils.ts +++ b/packages/calcite-components/src/tests/utils.ts @@ -268,7 +268,9 @@ export async function newProgrammaticE2EPage(): Promise { */ export async function skipAnimations(page: E2EPage): Promise { await page.addStyleTag({ - content: `:root { --calcite-duration-factor: 0; }` + // using 0.01 to ensure `openCloseComponent` utils work consistently + // this should be removed once https://github.com/Esri/calcite-components/issues/6604 is addressed + content: `:root { --calcite-duration-factor: 0.01; }` }); }