From e74a9cae56904c30c43b50b013eeb1c93713a910 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Mon, 4 Aug 2025 16:28:25 +0300 Subject: [PATCH 1/9] fix(ui5-calendar): remove auto-focus logic in onAfterRendering() hook --- packages/main/src/Calendar.ts | 65 +++++++++++------------ packages/main/src/CalendarTemplate.tsx | 1 + packages/main/src/DayPicker.ts | 41 +++++++------- packages/main/src/DayPickerTemplate.tsx | 3 +- packages/main/src/MonthPicker.ts | 41 +++++++++++--- packages/main/src/MonthPickerTemplate.tsx | 2 + packages/main/src/YearPicker.ts | 6 --- packages/main/src/YearRangePicker.ts | 6 --- 8 files changed, 93 insertions(+), 72 deletions(-) diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index e65249a7298a..172b686b124c 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -49,12 +49,11 @@ import CalendarHeaderCss from "./generated/themes/CalendarHeader.css.js"; import { CALENDAR_HEADER_NEXT_BUTTON, CALENDAR_HEADER_PREVIOUS_BUTTON } from "./generated/i18n/i18n-defaults.js"; import type { YearRangePickerChangeEventDetail } from "./YearRangePicker.js"; -interface ICalendarPicker { - _showPreviousPage: () => void, - _showNextPage: () => void, +interface ICalendarPicker extends HTMLElement { + _showPreviousPage: () => void | Promise, + _showNextPage: () => void | Promise, _hasPreviousPage: () => boolean, _hasNextPage: () => boolean, - _autoFocus?: boolean, _currentYearRange?: CalendarYearRangeT, } @@ -335,6 +334,15 @@ class Calendar extends CalendarPart { this._valueIsProcessed = false; } + async _focusCurrentPicker() { + await renderFinished(); + this._currentPickerDOM.focus(); + } + + async _onfocusin() { + await this._focusCurrentPicker(); + } + /** * @private */ @@ -465,7 +473,6 @@ class Calendar extends CalendarPart { if (defaultTypes.includes(this._selectedItemType)) { this._selectedItemType = "None"; // In order to avoid filtering of default types } - this._currentPickerDOM._autoFocus = false; } /** @@ -524,40 +531,40 @@ class Calendar extends CalendarPart { /** * The user clicked the "month" button in the header */ - onHeaderShowMonthPress() { - this.showMonth(); + async onHeaderShowMonthPress() { + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } - showMonth() { - this._currentPickerDOM._autoFocus = false; + async showMonth() { this._currentPicker = "month"; + await this._focusCurrentPicker(); } /** * The user clicked the "year" button in the header */ - onHeaderShowYearPress() { - this.showYear(); + async onHeaderShowYearPress() { + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } - showYear() { - this._currentPickerDOM._autoFocus = false; + async showYear() { this._currentPicker = "year"; + await this._focusCurrentPicker(); } /** * The user clicked the "year range" button in the YearPicker header */ - onHeaderShowYearRangePress() { - this.showYearRange(); + async onHeaderShowYearRangePress() { + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } - showYearRange() { - this._currentPickerDOM._autoFocus = false; + async showYearRange() { this._currentPicker = "yearrange"; + await this._focusCurrentPicker(); } get _currentPickerDOM() { @@ -570,10 +577,6 @@ class Calendar extends CalendarPart { */ onHeaderPreviousPress() { this._currentPickerDOM._showPreviousPage(); - - if (this.calendarLegend) { - this._currentPickerDOM._autoFocus = true; - } } /** @@ -581,10 +584,6 @@ class Calendar extends CalendarPart { */ onHeaderNextPress() { this._currentPickerDOM._showNextPage(); - - if (this.calendarLegend) { - this._currentPickerDOM._autoFocus = true; - } } _setSecondaryCalendarTypeButtonText() { @@ -710,40 +709,40 @@ class Calendar extends CalendarPart { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - onSelectedMonthChange(e: CustomEvent) { + async onSelectedMonthChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - - this._currentPickerDOM._autoFocus = true; } - onSelectedYearChange(e: CustomEvent) { + async onSelectedYearChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; + await this._focusCurrentPicker(); } else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) { this._currentPicker = "month"; + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - - this._currentPickerDOM._autoFocus = true; } - onSelectedYearRangeChange(e: CustomEvent) { + async onSelectedYearRangeChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; this._currentPicker = "year"; - this._currentPickerDOM._autoFocus = true; + await this._focusCurrentPicker(); } onNavigate(e: CustomEvent) { this.timestamp = e.detail.timestamp; + this._focusCurrentPicker(); } _onkeydown(e: KeyboardEvent) { diff --git a/packages/main/src/CalendarTemplate.tsx b/packages/main/src/CalendarTemplate.tsx index 68ea2ec153fa..925d24890480 100644 --- a/packages/main/src/CalendarTemplate.tsx +++ b/packages/main/src/CalendarTemplate.tsx @@ -12,6 +12,7 @@ export default function CalendarTemplate(this: Calendar) {
dayName.length > 4); } - onAfterRendering() { - if (this._autoFocus && !this._hidden) { - this.focus(); - } - } - _focusCorrectDay() { if (this._shouldFocusDay) { this._focusableDay.focus(); @@ -420,15 +413,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return document.activeElement !== this._focusableDay && this._specialCalendarDates.length === 0; } - _onfocusin() { - this._autoFocus = true; - this._focusCorrectDay(); - } - - _onfocusout() { - this._autoFocus = false; - } - /** * Tells if the day is selected (dark blue). * @param timestamp @@ -618,6 +602,21 @@ class DayPicker extends CalendarPart implements ICalendarPicker { } } + /** + * Sets the focus reference to the day that was clicked with mousedown. + * @param e + * @private + */ + _onmousedown(e: MouseEvent) { + const target = e.target as HTMLElement; + const clickedItem = target.closest(".ui5-dp-item") as HTMLElement; + + if (clickedItem && this._isDayPressed(clickedItem)) { + const timestamp = this._getTimestampFromDom(clickedItem); + this._setTimestamp(timestamp); + } + } + _onkeydown(e: KeyboardEvent) { let preventDefault = true; @@ -723,16 +722,20 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Called by the Calendar component. * @protected */ - _showPreviousPage() { + async _showPreviousPage() { this._modifyTimestampBy(-1, "month", false); + await renderFinished(); + this._focusCorrectDay(); } /** * Called by the Calendar component. * @protected */ - _showNextPage() { + async _showNextPage() { this._modifyTimestampBy(1, "month", false); + await renderFinished(); + this._focusCorrectDay(); } /** diff --git a/packages/main/src/DayPickerTemplate.tsx b/packages/main/src/DayPickerTemplate.tsx index 6e9735d12493..867b0b27d1bd 100644 --- a/packages/main/src/DayPickerTemplate.tsx +++ b/packages/main/src/DayPickerTemplate.tsx @@ -14,9 +14,8 @@ export default function DayPickerTemplate(this: DayPicker) { onKeyDown={this._onkeydown} onKeyUp={this._onkeyup} onClick={this._onclick} + onMouseDown={this._onmousedown} onMouseOver={this._onmouseover} - onFocusIn={this._onfocusin} - onFocusOut={this._onfocusout} >
diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index 4c4065ad0a29..03ff6abb27a9 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -1,5 +1,6 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import query from "@ui5/webcomponents-base/dist/decorators/query.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import getCachedLocaleDataInstance from "@ui5/webcomponents-localization/dist/getCachedLocaleDataInstance.js"; @@ -35,6 +36,7 @@ import MonthPickerTemplate from "./MonthPickerTemplate.js"; // Styles import monthPickerStyles from "./generated/themes/MonthPicker.css.js"; import CalendarSelectionMode from "./types/CalendarSelectionMode.js"; +import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; const isBetween = (x: number, num1: number, num2: number) => x > Math.min(num1, num2) && x < Math.max(num1, num2); const PAGE_SIZE = 12; // total months on a single page @@ -132,6 +134,9 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { @property({ type: Number }) _secondTimestamp?: number; + @query("[data-sap-focus-ref]") + _focusableMonth!: HTMLElement; + @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; @@ -143,17 +148,26 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { this._buildMonths(); } - onAfterRendering() { - if (!this._hidden) { - this.focus(); - } - } - get rowSize() { return (this.secondaryCalendarType === CalendarType.Islamic && this.primaryCalendarType !== CalendarType.Islamic) || (this.secondaryCalendarType === CalendarType.Persian && this.primaryCalendarType !== CalendarType.Persian) ? 2 : 3; } + _focusCorrectMonth() { + if (this._shouldFocusMonth) { + this._focusableMonth.focus(); + } + } + + get _shouldFocusMonth() { + return document.activeElement !== this._focusableMonth; + } + + async _onfocusin() { + await renderFinished(); + this._focusCorrectMonth(); + } + _buildMonths() { if (this._hidden) { return; @@ -330,6 +344,21 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { } } + /** + * Sets the focus reference to the month that was clicked with mousedown. + * @param e + * @private + */ + _onmousedown(e: MouseEvent) { + const target = e.target as HTMLElement; + const clickedItem = target.closest(".ui5-mp-item") as HTMLElement; + + if (clickedItem) { + const timestamp = this._getTimestampFromDom(clickedItem); + this._setTimestamp(timestamp); + } + } + /** * Modifies timestamp by a given amount of months and, * if necessary, loads the prev/next page. diff --git a/packages/main/src/MonthPickerTemplate.tsx b/packages/main/src/MonthPickerTemplate.tsx index d4a3a95e16af..435f9a9aebb2 100644 --- a/packages/main/src/MonthPickerTemplate.tsx +++ b/packages/main/src/MonthPickerTemplate.tsx @@ -13,6 +13,8 @@ export default function MonthPickerTemplate(this: MonthPicker) { onKeyDown={this._onkeydown} onKeyUp={this._onkeyup} onClick={this._selectMonth} + onMouseDown={this._onmousedown} + onFocusIn={this._onfocusin} > {this._monthsInterval.map(months =>
diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index d359cd24f711..e837ae2e7232 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -240,12 +240,6 @@ class YearPicker extends CalendarPart implements ICalendarPicker { this._yearsInterval = intervals; } - onAfterRendering() { - if (!this._hidden) { - this.focus(); - } - } - /** * Returns true if year timestamp is inside the selection range. * @private diff --git a/packages/main/src/YearRangePicker.ts b/packages/main/src/YearRangePicker.ts index 8802ae52eac6..07b6086cca6e 100644 --- a/packages/main/src/YearRangePicker.ts +++ b/packages/main/src/YearRangePicker.ts @@ -313,12 +313,6 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { return isBetweenInclusive(timestamp, this.selectedDates[0], this.selectedDates[1]); } - onAfterRendering() { - if (!this._hidden) { - this.focus(); - } - } - _onkeydown(e: KeyboardEvent) { let preventDefault = true; const pageSize = this._getPageSize(); From 3edec0abea1dff94531dce5ba0153366607ae3d1 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Tue, 5 Aug 2025 14:31:00 +0300 Subject: [PATCH 2/9] fix: fix tests, remove focus locked in calendar --- packages/main/cypress/specs/Calendar.cy.tsx | 2 ++ packages/main/src/Calendar.ts | 7 +++---- packages/main/src/CalendarTemplate.tsx | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/main/cypress/specs/Calendar.cy.tsx b/packages/main/cypress/specs/Calendar.cy.tsx index f438ca2034b2..676c95894e33 100644 --- a/packages/main/cypress/specs/Calendar.cy.tsx +++ b/packages/main/cypress/specs/Calendar.cy.tsx @@ -136,6 +136,8 @@ describe("Calendar general interaction", () => { cy.ui5CalendarGetDay("#calendar1", "974851200") .realClick(); + cy.focused().realPress("Tab"); + cy.focused().realPress("Tab"); cy.focused().realPress("Tab"); cy.focused().realPress("Tab"); cy.focused().realPress("Space"); diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index 172b686b124c..2eb791374cac 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -339,10 +339,6 @@ class Calendar extends CalendarPart { this._currentPickerDOM.focus(); } - async _onfocusin() { - await this._focusCurrentPicker(); - } - /** * @private */ @@ -749,6 +745,7 @@ class Calendar extends CalendarPart { if (isF4(e) && this._currentPicker !== "month") { this._currentPicker = "month"; this.fireDecoratorEvent("show-month-view"); + this._focusCurrentPicker(); } if (!isF4Shift(e)) { @@ -758,9 +755,11 @@ class Calendar extends CalendarPart { if (this._currentPicker !== "year") { this._currentPicker = "year"; this.fireDecoratorEvent("show-year-view"); + this._focusCurrentPicker(); } else { this._currentPicker = "yearrange"; this.fireDecoratorEvent("show-year-range-view"); + this._focusCurrentPicker(); } } diff --git a/packages/main/src/CalendarTemplate.tsx b/packages/main/src/CalendarTemplate.tsx index 925d24890480..68ea2ec153fa 100644 --- a/packages/main/src/CalendarTemplate.tsx +++ b/packages/main/src/CalendarTemplate.tsx @@ -12,7 +12,6 @@ export default function CalendarTemplate(this: Calendar) {
Date: Fri, 8 Aug 2025 10:08:02 +0300 Subject: [PATCH 3/9] fix: fix dp test --- packages/main/src/DayPicker.ts | 10 +++++++++- packages/main/src/MonthPicker.ts | 5 ++++- packages/main/src/YearPicker.ts | 15 ++++++++++++++- packages/main/src/YearRangePicker.ts | 15 ++++++++++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index f5b5ba74d63d..1513e0c63800 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -413,6 +413,11 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return document.activeElement !== this._focusableDay && this._specialCalendarDates.length === 0; } + async _onfocusin() { + await renderFinished(); + this._focusCorrectDay(); + } + /** * Tells if the day is selected (dark blue). * @param timestamp @@ -745,13 +750,16 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { + async _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, unit, preserveDate); this._updateSecondTimestamp(); // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); + + await renderFinished(); + this._focusCorrectDay(); } /** diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index 03ff6abb27a9..dd5570f62267 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -366,13 +366,16 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - _modifyTimestampBy(amount: number, preserveDate?: boolean) { + async _modifyTimestampBy(amount: number, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "month", preserveDate); this._updateSecondTimestamp(); // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); + + await renderFinished(); + this._focusCorrectMonth(); } _onkeyup(e: KeyboardEvent) { diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index e837ae2e7232..ed7c136c13bb 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -1,7 +1,9 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import query from "@ui5/webcomponents-base/dist/decorators/query.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; import type LocaleT from "sap/ui/core/Locale"; import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js"; import { @@ -133,6 +135,9 @@ class YearPicker extends CalendarPart implements ICalendarPicker { _firstYear?: number; + @query("[data-sap-focus-ref]") + _focusableYear!: HTMLElement; + @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; @@ -140,6 +145,11 @@ class YearPicker extends CalendarPart implements ICalendarPicker { return YearPicker.i18nBundle.getText(YEAR_PICKER_DESCRIPTION); } + async _onfocusin() { + await renderFinished(); + this._focusableYear.focus(); + } + onBeforeRendering() { if (this._hidden) { return; @@ -342,13 +352,16 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * @param amount * @private */ - _modifyTimestampBy(amount: number) { + async _modifyTimestampBy(amount: number) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "year"); this._updateSecondTimestamp(); // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); + + await renderFinished(); + this._focusableYear.focus(); } _onkeyup(e: KeyboardEvent) { diff --git a/packages/main/src/YearRangePicker.ts b/packages/main/src/YearRangePicker.ts index 07b6086cca6e..ce45d4ef755d 100644 --- a/packages/main/src/YearRangePicker.ts +++ b/packages/main/src/YearRangePicker.ts @@ -1,7 +1,9 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import query from "@ui5/webcomponents-base/dist/decorators/query.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; import type LocaleT from "sap/ui/core/Locale"; import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js"; import { @@ -128,6 +130,9 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { _gridStartYear?: number; + @query("[data-sap-focus-ref]") + _focusableYearRange!: HTMLElement; + @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; @@ -135,6 +140,11 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { return YearRangePicker.i18nBundle.getText(YEAR_RANGE_PICKER_DESCRIPTION); } + async _onfocusin() { + await renderFinished(); + this._focusableYearRange.focus(); + } + onBeforeRendering() { if (this._hidden) { return; @@ -488,13 +498,16 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { * @param amount * @private */ - _modifyTimestampBy(amount: number) { + async _modifyTimestampBy(amount: number) { // Modify the current timestamp const amountInYears = amount * this._getRangeSize(); this._safelyModifyTimestampBy(amountInYears, "year"); // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); + + await renderFinished(); + this._focusableYearRange.focus(); } _modifyGridStartBy(years: number) { From 1577502b5cdd9838c625405cfabd802cdeb3f20f Mon Sep 17 00:00:00 2001 From: Stoyan Date: Mon, 11 Aug 2025 08:32:44 +0300 Subject: [PATCH 4/9] fix: address comments --- packages/main/cypress/specs/Calendar.cy.tsx | 2 -- packages/main/src/Calendar.ts | 4 ++-- packages/main/src/DayPicker.ts | 15 ++++++--------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/main/cypress/specs/Calendar.cy.tsx b/packages/main/cypress/specs/Calendar.cy.tsx index 676c95894e33..f438ca2034b2 100644 --- a/packages/main/cypress/specs/Calendar.cy.tsx +++ b/packages/main/cypress/specs/Calendar.cy.tsx @@ -136,8 +136,6 @@ describe("Calendar general interaction", () => { cy.ui5CalendarGetDay("#calendar1", "974851200") .realClick(); - cy.focused().realPress("Tab"); - cy.focused().realPress("Tab"); cy.focused().realPress("Tab"); cy.focused().realPress("Tab"); cy.focused().realPress("Space"); diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index 2eb791374cac..046b3d0bcc2b 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -755,12 +755,12 @@ class Calendar extends CalendarPart { if (this._currentPicker !== "year") { this._currentPicker = "year"; this.fireDecoratorEvent("show-year-view"); - this._focusCurrentPicker(); } else { this._currentPicker = "yearrange"; this.fireDecoratorEvent("show-year-range-view"); - this._focusCurrentPicker(); } + + this._focusCurrentPicker(); } _onLegendFocusOut() { diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 1513e0c63800..de95a8a4a3b0 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -403,7 +403,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return dayNames.some(dayName => dayName.length > 4); } - _focusCorrectDay() { + async _focusCorrectDay() { + await renderFinished(); if (this._shouldFocusDay) { this._focusableDay.focus(); } @@ -413,8 +414,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return document.activeElement !== this._focusableDay && this._specialCalendarDates.length === 0; } - async _onfocusin() { - await renderFinished(); + _onfocusin() { this._focusCorrectDay(); } @@ -727,9 +727,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Called by the Calendar component. * @protected */ - async _showPreviousPage() { + _showPreviousPage() { this._modifyTimestampBy(-1, "month", false); - await renderFinished(); this._focusCorrectDay(); } @@ -737,9 +736,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Called by the Calendar component. * @protected */ - async _showNextPage() { + _showNextPage() { this._modifyTimestampBy(1, "month", false); - await renderFinished(); this._focusCorrectDay(); } @@ -750,7 +748,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - async _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { + _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, unit, preserveDate); this._updateSecondTimestamp(); @@ -758,7 +756,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - await renderFinished(); this._focusCorrectDay(); } From 463c2a3238d712a8d597fb49da3bf4af2ab6d756 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Wed, 13 Aug 2025 16:08:58 +0300 Subject: [PATCH 5/9] chore: address discussion comments --- packages/main/src/Calendar.ts | 38 ++++++++++++++-------------- packages/main/src/MonthPicker.ts | 9 +++---- packages/main/src/YearPicker.ts | 14 ++++++++-- packages/main/src/YearRangePicker.ts | 14 ++++++++-- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index 046b3d0bcc2b..c860ccec7dbd 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -527,40 +527,40 @@ class Calendar extends CalendarPart { /** * The user clicked the "month" button in the header */ - async onHeaderShowMonthPress() { - await this.showMonth(); + onHeaderShowMonthPress() { + this.showMonth(); this.fireDecoratorEvent("show-month-view"); } - async showMonth() { + showMonth() { this._currentPicker = "month"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } /** * The user clicked the "year" button in the header */ - async onHeaderShowYearPress() { - await this.showYear(); + onHeaderShowYearPress() { + this.showYear(); this.fireDecoratorEvent("show-year-view"); } - async showYear() { + showYear() { this._currentPicker = "year"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } /** * The user clicked the "year range" button in the YearPicker header */ - async onHeaderShowYearRangePress() { - await this.showYearRange(); + onHeaderShowYearRangePress() { + this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } - async showYearRange() { + showYearRange() { this._currentPicker = "yearrange"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } get _currentPickerDOM() { @@ -705,35 +705,35 @@ class Calendar extends CalendarPart { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - async onSelectedMonthChange(e: CustomEvent) { + onSelectedMonthChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - async onSelectedYearChange(e: CustomEvent) { + onSelectedYearChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) { this._currentPicker = "month"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - async onSelectedYearRangeChange(e: CustomEvent) { + onSelectedYearRangeChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; this._currentPicker = "year"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } onNavigate(e: CustomEvent) { diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index dd5570f62267..8c468c230b14 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -153,7 +153,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { || (this.secondaryCalendarType === CalendarType.Persian && this.primaryCalendarType !== CalendarType.Persian) ? 2 : 3; } - _focusCorrectMonth() { + async _focusCorrectMonth() { + await renderFinished(); if (this._shouldFocusMonth) { this._focusableMonth.focus(); } @@ -163,8 +164,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { return document.activeElement !== this._focusableMonth; } - async _onfocusin() { - await renderFinished(); + _onfocusin() { this._focusCorrectMonth(); } @@ -366,7 +366,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - async _modifyTimestampBy(amount: number, preserveDate?: boolean) { + _modifyTimestampBy(amount: number, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "month", preserveDate); this._updateSecondTimestamp(); @@ -374,7 +374,6 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - await renderFinished(); this._focusCorrectMonth(); } diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index ed7c136c13bb..7c97f1c373ae 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -145,9 +145,19 @@ class YearPicker extends CalendarPart implements ICalendarPicker { return YearPicker.i18nBundle.getText(YEAR_PICKER_DESCRIPTION); } - async _onfocusin() { + async _focusCorrectYear() { await renderFinished(); - this._focusableYear.focus(); + if (this._shouldFocusYear) { + this._focusableYear.focus(); + } + } + + get _shouldFocusYear() { + return document.activeElement !== this._focusableYear; + } + + _onfocusin() { + this._focusCorrectYear(); } onBeforeRendering() { diff --git a/packages/main/src/YearRangePicker.ts b/packages/main/src/YearRangePicker.ts index ce45d4ef755d..1bff005f0986 100644 --- a/packages/main/src/YearRangePicker.ts +++ b/packages/main/src/YearRangePicker.ts @@ -140,9 +140,19 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { return YearRangePicker.i18nBundle.getText(YEAR_RANGE_PICKER_DESCRIPTION); } - async _onfocusin() { + async _focusCorrectYearRange() { await renderFinished(); - this._focusableYearRange.focus(); + if (this._shouldFocusYearRange) { + this._focusableYearRange.focus(); + } + } + + get _shouldFocusYearRange() { + return document.activeElement !== this._focusableYearRange; + } + + _onfocusin() { + this._focusCorrectYearRange(); } onBeforeRendering() { From 0f0e3e3273559fbbff8425b74bfff3752eaed107 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Sat, 16 Aug 2025 10:05:48 +0300 Subject: [PATCH 6/9] chore: revert and add async/await to async methods --- packages/main/src/Calendar.ts | 74 ++++++++++++++-------------- packages/main/src/DayPicker.ts | 14 +++--- packages/main/src/MonthPicker.ts | 12 ++--- packages/main/src/YearPicker.ts | 8 +-- packages/main/src/YearRangePicker.ts | 8 +-- 5 files changed, 57 insertions(+), 59 deletions(-) diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index c860ccec7dbd..915044df3ee2 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -527,40 +527,40 @@ class Calendar extends CalendarPart { /** * The user clicked the "month" button in the header */ - onHeaderShowMonthPress() { - this.showMonth(); + async onHeaderShowMonthPress() { + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } - showMonth() { + async showMonth() { this._currentPicker = "month"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } /** * The user clicked the "year" button in the header */ - onHeaderShowYearPress() { - this.showYear(); + async onHeaderShowYearPress() { + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } - showYear() { + async showYear() { this._currentPicker = "year"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } /** * The user clicked the "year range" button in the YearPicker header */ - onHeaderShowYearRangePress() { - this.showYearRange(); + async onHeaderShowYearRangePress() { + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } - showYearRange() { + async showYearRange() { this._currentPicker = "yearrange"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } get _currentPickerDOM() { @@ -571,15 +571,15 @@ class Calendar extends CalendarPart { /** * The year clicked the "Previous" button in the header */ - onHeaderPreviousPress() { - this._currentPickerDOM._showPreviousPage(); + async onHeaderPreviousPress() { + await this._currentPickerDOM._showPreviousPage(); } /** * The year clicked the "Next" button in the header */ - onHeaderNextPress() { - this._currentPickerDOM._showNextPage(); + async onHeaderNextPress() { + await this._currentPickerDOM._showNextPage(); } _setSecondaryCalendarTypeButtonText() { @@ -705,40 +705,40 @@ class Calendar extends CalendarPart { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - onSelectedMonthChange(e: CustomEvent) { + async onSelectedMonthChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - onSelectedYearChange(e: CustomEvent) { + async onSelectedYearChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) { this._currentPicker = "month"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - onSelectedYearRangeChange(e: CustomEvent) { + async onSelectedYearRangeChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; this._currentPicker = "year"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } - onNavigate(e: CustomEvent) { + async onNavigate(e: CustomEvent) { this.timestamp = e.detail.timestamp; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } _onkeydown(e: KeyboardEvent) { @@ -805,57 +805,57 @@ class Calendar extends CalendarPart { return secondMonthButtonText; } - onMonthButtonKeyDown(e: KeyboardEvent) { + async onMonthButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showMonth(); + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - onMonthButtonKeyUp(e: KeyboardEvent) { + async onMonthButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); - this.showMonth(); + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - onYearButtonKeyDown(e: KeyboardEvent) { + async onYearButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showYear(); + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - onYearButtonKeyUp(e: KeyboardEvent) { + async onYearButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - this.showYear(); + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - onYearRangeButtonKeyDown(e: KeyboardEvent) { + async onYearRangeButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showYearRange(); + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } - onYearRangeButtonKeyUp(e: KeyboardEvent) { + async onYearRangeButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - this.showYearRange(); + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index de95a8a4a3b0..26298d705c29 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -727,18 +727,16 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Called by the Calendar component. * @protected */ - _showPreviousPage() { - this._modifyTimestampBy(-1, "month", false); - this._focusCorrectDay(); + async _showPreviousPage() { + await this._modifyTimestampBy(-1, "month", false); } /** * Called by the Calendar component. * @protected */ - _showNextPage() { - this._modifyTimestampBy(1, "month", false); - this._focusCorrectDay(); + async _showNextPage() { + await this._modifyTimestampBy(1, "month", false); } /** @@ -748,7 +746,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { + async _modifyTimestampBy(amount: number, unit: string, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, unit, preserveDate); this._updateSecondTimestamp(); @@ -756,7 +754,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - this._focusCorrectDay(); + await this._focusCorrectDay(); } /** diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index 8c468c230b14..bf622a2a16d6 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -366,7 +366,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - _modifyTimestampBy(amount: number, preserveDate?: boolean) { + async _modifyTimestampBy(amount: number, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "month", preserveDate); this._updateSecondTimestamp(); @@ -374,7 +374,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - this._focusCorrectMonth(); + await this._focusCorrectMonth(); } _onkeyup(e: KeyboardEvent) { @@ -438,8 +438,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - _showPreviousPage() { - this._modifyTimestampBy(-PAGE_SIZE, true); + async _showPreviousPage() { + await this._modifyTimestampBy(-PAGE_SIZE, true); } /** @@ -447,8 +447,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - _showNextPage() { - this._modifyTimestampBy(PAGE_SIZE, true); + async _showNextPage() { + await this._modifyTimestampBy(PAGE_SIZE, true); } _isOutOfSelectableRange(date: CalendarDate, minDate: CalendarDate, maxDate: CalendarDate): boolean { diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index 7c97f1c373ae..946dce567a1e 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -434,9 +434,9 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - _showPreviousPage() { + async _showPreviousPage() { const pageSize = this._getPageSize(); - this._modifyTimestampBy(-pageSize); + await this._modifyTimestampBy(-pageSize); } /** @@ -444,8 +444,8 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - _showNextPage() { - this._modifyTimestampBy(this._getPageSize()); + async _showNextPage() { + await this._modifyTimestampBy(this._getPageSize()); } } diff --git a/packages/main/src/YearRangePicker.ts b/packages/main/src/YearRangePicker.ts index 1bff005f0986..f64bbdc3f0f5 100644 --- a/packages/main/src/YearRangePicker.ts +++ b/packages/main/src/YearRangePicker.ts @@ -482,9 +482,9 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - _showPreviousPage() { + async _showPreviousPage() { const pageSize = this._getPageSize(); - this._modifyTimestampBy(-pageSize); + await this._modifyTimestampBy(-pageSize); const amountInYears = pageSize * this._getRangeSize(); this._modifyGridStartBy(-amountInYears); @@ -495,9 +495,9 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - _showNextPage() { + async _showNextPage() { const pageSize = this._getPageSize(); - this._modifyTimestampBy(pageSize); + await this._modifyTimestampBy(pageSize); const amountInYears = pageSize * this._getRangeSize(); this._modifyGridStartBy(amountInYears); From edd1519efa2ff8e597b0f3b73353678334d1e644 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Tue, 19 Aug 2025 14:48:05 +0300 Subject: [PATCH 7/9] fix: stabilize tests by reordering them --- packages/main/cypress/specs/Calendar.cy.tsx | 308 +++++++++--------- packages/main/cypress/specs/DatePicker.cy.tsx | 134 ++++---- .../main/cypress/specs/DateRangePicker.cy.tsx | 121 +++++-- packages/main/src/Calendar.ts | 100 +++--- packages/main/src/MonthPicker.ts | 12 +- packages/main/src/YearPicker.ts | 13 +- 6 files changed, 388 insertions(+), 300 deletions(-) diff --git a/packages/main/cypress/specs/Calendar.cy.tsx b/packages/main/cypress/specs/Calendar.cy.tsx index f438ca2034b2..282c62e669a3 100644 --- a/packages/main/cypress/specs/Calendar.cy.tsx +++ b/packages/main/cypress/specs/Calendar.cy.tsx @@ -36,46 +36,67 @@ const getCalendarsWithWeekNumbers = () => (<> ); describe("Calendar general interaction", () => { - it("Focus goes into the current day item of the day picker", () => { - const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); - cy.mount(getDefaultCalendar(date)); + it("Focus goes into the current day item of the day picker –– UNSTABLE", () => { + const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); + cy.mount(getDefaultCalendar(date)); - cy.ui5CalendarGetDay("#calendar1", "974851200") - .as("selectedDay"); + cy.ui5CalendarGetDay("#calendar1", "974851200") + .as("selectedDay"); - cy.get("@selectedDay") - .realClick(); + cy.get("@selectedDay") + .realClick(); - cy.get("@selectedDay") - .should("have.focus") - .realPress("Tab"); + cy.get("@selectedDay") + .should("have.focus") + .realPress("Tab", { pressDelay: 1 }); + + cy.get("#calendar1") + .shadow() + .find(".ui5-calheader") + .as("calheader"); + + cy.get("@calheader") + .find("[data-ui5-cal-header-btn-month]") + .as("monthBtn"); + + cy.get("@monthBtn") + .should("have.focus") + .realPress("Tab", { pressDelay: 1 }); + + cy.get("@calheader") + .find("[data-ui5-cal-header-btn-year]") + .as("yearBtn"); + + cy.get("@yearBtn") + .should("have.focus") + .realPress(["Shift", "Tab"], { pressDelay: 1 }); + + cy.get("@monthBtn") + .should("have.focus") + .realPress(["Shift", "Tab"], { pressDelay: 1 }); + + cy.get("@selectedDay") + .should("have.focus"); + }); + + it("Focus goes into first selected day of the range selection –– UNSTABLE", () => { + cy.mount( + + + ); + + const timestamp = new Date(Date.UTC(2021, 0, 20, 0, 0, 0)).valueOf() / 1000; // 1611100800 cy.get("#calendar1") .shadow() .find(".ui5-calheader") - .as("calheader"); - - cy.get("@calheader") .find("[data-ui5-cal-header-btn-month]") - .as("monthBtn"); - - cy.get("@monthBtn") - .should("have.focus") - .realPress("Tab"); - - cy.get("@calheader") - .find("[data-ui5-cal-header-btn-year]") - .as("yearBtn"); - - cy.get("@yearBtn") - .should("have.focus") - .realPress(["Shift", "Tab"]); + .realClick(); - cy.get("@monthBtn") - .should("have.focus") - .realPress(["Shift", "Tab"]); + cy.ui5CalendarGetMonth("#calendar1", timestamp.toString()) + .realClick(); - cy.get("@selectedDay") + cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) .should("have.focus"); }); @@ -129,34 +150,6 @@ describe("Calendar general interaction", () => { }); }); - it("Calendar focuses the selected year when yearpicker is opened with space", () => { - const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); - cy.mount(getDefaultCalendar(date)); - - cy.ui5CalendarGetDay("#calendar1", "974851200") - .realClick(); - - cy.focused().realPress("Tab"); - cy.focused().realPress("Tab"); - cy.focused().realPress("Space"); - - cy.get("#calendar1") - .shadow() - .find("[ui5-yearpicker]") - .should("not.have.attr", "hidden"); - - cy.get("#calendar1") - .shadow() - .find("[ui5-yearpicker]") - .shadow() - .find("[tabindex='0']") - .invoke("attr", "data-sap-timestamp") - .then(_timestamp => { - const focusedYear = new Date(parseInt(_timestamp!) * 1000).getUTCFullYear(); - expect(focusedYear).to.equal(2000); - }); - }); - it("Should focus the selected range when YearRange Picker is opened", () => { const YEAR = 1997; const date = Date.UTC(YEAR); @@ -286,8 +279,8 @@ describe("Calendar general interaction", () => { }); - it("Page up/down increments/decrements the month value", () => { - const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); + it("Page up/down increments/decrements the year range in the year picker – UNSTABLE", () => { + const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") @@ -297,12 +290,13 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); + cy.focused().realPress(["Shift", "F4"]); cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1980, 9, 1, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -310,39 +304,34 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); }); - it("Shift + Page up/down increments/decrements the year value by one", () => { - const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); + + it("Navigation with Page down decrements the year ranges in the year range picker –– UNSTABLE", () => { + const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .shadow() - .find("[ui5-daypicker]") - .shadow() - .find("[tabindex='0']") - .realClick(); - - cy.focused().realPress(["Shift", "PageUp"]); + .ui5CalendarShowYearRangePicker(); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); - cy.focused().realPress(["Shift", "PageDown"]); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1838, 9, 16, 0, 0, 0))); }); }); - it("Ctrl + Shift + Page up/down increments/decrements the year value by ten", () => { + it("Page up/down increments/decrements the month value", () => { const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); @@ -353,15 +342,15 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); - cy.focused().realPress(["Control", "Shift", "PageUp"]); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1990, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); - cy.focused().realPress(["Control", "Shift", "PageDown"]); + cy.focused().realPress("PageDown"); cy.get("#calendar1") .invoke("prop", "timestamp") @@ -370,24 +359,17 @@ describe("Calendar general interaction", () => { }); }); - it("Page up/down increments/decrements the year value in the month picker", () => { - const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); + it("Navigation with Page up increments the year ranges in the year range picker –– UNSTABLE", () => { + const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .shadow() - .find("[ui5-daypicker]") - .shadow() - .find("[tabindex='0']") - .realClick(); - - cy.focused().realPress("F4"); - cy.focused().realPress("PageUp"); + .ui5CalendarShowYearRangePicker(); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -395,12 +377,12 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2158, 9, 16, 0, 0, 0))); }); }); - it("Page up/down increments/decrements the year range in the year picker", () => { - const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); + it("Shift + Page up/down increments/decrements the year value by one", () => { + const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") @@ -410,57 +392,69 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); - cy.focused().realPress(["Shift", "F4"]); - cy.focused().realPress("PageUp"); + cy.focused().realPress(["Shift", "PageUp"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1980, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 10, 1, 0, 0, 0))); }); - cy.focused().realPress("PageDown"); + cy.focused().realPress(["Shift", "PageDown"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); }); }); - it("Navigation with Page down decrements the year ranges in the year range picker", () => { - const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); + it("Ctrl + Shift + Page up/down increments/decrements the year value by ten", () => { + const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .ui5CalendarShowYearRangePicker(); + .shadow() + .find("[ui5-daypicker]") + .shadow() + .find("[tabindex='0']") + .realClick(); + + cy.focused().realPress(["Control", "Shift", "PageUp"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1990, 10, 1, 0, 0, 0))); }); - cy.focused().realPress("PageUp"); + cy.focused().realPress(["Control", "Shift", "PageDown"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1838, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); }); }); - it("Navigation with Page up increments the year ranges in the year range picker", () => { - const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); + it("Page up/down increments/decrements the year value in the month picker", () => { + const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .ui5CalendarShowYearRangePicker(); + .shadow() + .find("[ui5-daypicker]") + .shadow() + .find("[tabindex='0']") + .realClick(); + + cy.focused().realPress("F4"); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 9, 1, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -468,7 +462,7 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2158, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); }); @@ -495,27 +489,6 @@ describe("Calendar general interaction", () => { }); }); - it("Keyboard navigation works properly, when calendar selection type is set to 'Multiple'", () => { - cy.mount(); - - const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; - - cy.get("#calendar1").invoke("prop", "timestamp", timestamp); - - cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) - .focus(); - - // Select the focused date - cy.focused().realPress("Space"); - - // Deselect the focused date - cy.focused().realPress("Space"); - cy.focused().realPress("ArrowRight"); - - cy.ui5CalendarGetDay("#calendar1", "971222400") - .should("have.focus"); - }); - it("Calendar with 'Range' selection type", () => { cy.mount(); const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; @@ -546,6 +519,27 @@ describe("Calendar general interaction", () => { }); }); + it("Keyboard navigation works properly, when calendar selection type is set to 'Multiple' –– UNSTABLE", () => { + cy.mount(); + + const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; + + cy.get("#calendar1").invoke("prop", "timestamp", timestamp); + + cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) + .realClick(); + + // Select the focused date + cy.focused().realPress("Space"); + + // Deselect the focused date + cy.focused().realPress("Space"); + cy.focused().realPress("ArrowRight"); + + cy.ui5CalendarGetDay("#calendar1", "971222400") + .should("have.focus"); + }); + it("Previous and next buttons are disabled when necessary", () => { cy.mount(<> @@ -717,7 +711,7 @@ describe("Calendar general interaction", () => { expect(spans[0]).to.have.text("1414 AH"); expect(spans[1]).to.have.text("1993 - 1994"); }); - }); + }); it("Calendar renders secondary type for Year Range Picker", () => { cy.mount( @@ -803,6 +797,35 @@ describe("Calendar general interaction", () => { .should("not.have.class", "ui5-yp-item--disabled"); }); + + it("Calendar focuses the selected year when yearpicker is opened with space –– UNSTABLE", () => { + const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); + cy.mount(getDefaultCalendar(date)); + + cy.ui5CalendarGetDay("#calendar1", "974851200") + .realClick(); + + cy.focused().realPress("Tab", { pressDelay: 1 }); + cy.focused().realPress("Tab", { pressDelay: 1 }); + cy.focused().realPress("Space", { pressDelay: 1 }); + + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .should("not.have.attr", "hidden"); + + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find("[tabindex='0']") + .invoke("attr", "data-sap-timestamp") + .then(_timestamp => { + const focusedYear = new Date(parseInt(_timestamp!) * 1000).getUTCFullYear(); + expect(focusedYear).to.equal(2000); + }); + }); + it("Year Ranges outside of the min and max dates are disabled", () => { cy.mount( { .should("have.class", "ui5-yrp-item--disabled"); }); - it("Focus goes into first selected day of the range selection", () => { - cy.mount( - - - ); - - const timestamp = new Date(Date.UTC(2021, 0, 20, 0, 0, 0)).valueOf() / 1000; // 1611100800 - - cy.get("#calendar1") - .shadow() - .find(".ui5-calheader") - .find("[data-ui5-cal-header-btn-month]") - .realClick(); - - cy.ui5CalendarGetMonth("#calendar1", timestamp.toString()) - .realClick(); - - cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) - .should("have.focus"); - }); - it("Special date respects format-pattern given to the calendar", () => { cy.mount( @@ -957,3 +959,7 @@ describe("Calendar general interaction", () => { .should("have.text", "Sun"); }); }); + +describe("Calendar - UNSTABLE TESTS", () => { + +}); diff --git a/packages/main/cypress/specs/DatePicker.cy.tsx b/packages/main/cypress/specs/DatePicker.cy.tsx index ee3bd557aee6..d6344170b22d 100644 --- a/packages/main/cypress/specs/DatePicker.cy.tsx +++ b/packages/main/cypress/specs/DatePicker.cy.tsx @@ -3,6 +3,7 @@ import "../../src/Assets.js"; import { setLanguage } from "@ui5/webcomponents-base/dist/config/Language.js"; import DatePicker from "../../src/DatePicker.js"; import Label from "../../src/Label.js"; +import Input from "../../src/Input.js"; describe("Date Picker Tests", () => { it("input renders", () => { @@ -268,6 +269,35 @@ describe("Date Picker Tests", () => { .should("have.value", ""); }); + it("change fires when we change the input back to its original value", () => { + cy.mount(); + + cy.get("[ui5-date-picker]") + .as("datePicker") + .ui5DatePickerGetInnerInput() + .as("input") + .realClick() + .should("be.focused"); + + cy.get("@input") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Enter"); + + cy.get("@datePicker") + .should("have.value", ""); + + cy.get("@input") + .should("be.focused") + .realType("2015") + .realPress("Enter"); + + cy.get("@datePicker") + .should("have.value", "2015"); + }); + it("respect first day of the week - monday", () => { cy.wrap({ setLanguage }) .invoke("setLanguage", "bg"); @@ -326,35 +356,6 @@ describe("Date Picker Tests", () => { .should("have.attr", "open"); }); - it("change fires when we change the input back to its original value", () => { - cy.mount(); - - cy.get("[ui5-date-picker]") - .as("datePicker") - .ui5DatePickerGetInnerInput() - .as("input") - .realClick() - .should("be.focused"); - - cy.get("@input") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Enter"); - - cy.get("@datePicker") - .should("have.value", ""); - - cy.get("@input") - .should("be.focused") - .realType("2015") - .realPress("Enter"); - - cy.get("@datePicker") - .should("have.value", "2015"); - }); - it("change fires every time tomorrow is typed and normalized", () => { cy.mount(); @@ -392,6 +393,25 @@ describe("Date Picker Tests", () => { .should("have.class", "ui5-dp-item--selected"); }); + it("[Shift] + [F4] shows year picker after date picker is open", () => { + cy.mount(); + + cy.get("[ui5-date-picker]") + .as("datePicker") + .ui5DatePickerValueHelpIconPress(); + + cy.get("@datePicker") + .shadow() + .find("ui5-calendar") + .as("calendar") + .realPress(["Shift", "F4"]); + + cy.get("@calendar") + .shadow() + .find("ui5-yearpicker") + .should("be.visible"); + }); + it("[F4] toggles the calendar", () => { cy.mount(); @@ -485,13 +505,15 @@ describe("Date Picker Tests", () => { .should("be.visible"); }); - it("[Shift] + [F4] shows year picker after date picker is open", () => { + it("[F4] shows month picker after year picker is open", () => { cy.mount(); cy.get("[ui5-date-picker]") .as("datePicker") .ui5DatePickerValueHelpIconPress(); + cy.wait(150) + cy.get("@datePicker") .shadow() .find("ui5-calendar") @@ -499,23 +521,6 @@ describe("Date Picker Tests", () => { .realPress(["Shift", "F4"]); cy.get("@calendar") - .shadow() - .find("ui5-yearpicker") - .should("be.visible"); - }); - - it("[F4] shows month picker after year picker is open", () => { - cy.mount(); - - cy.get("[ui5-date-picker]") - .as("datePicker") - .ui5DatePickerValueHelpIconPress(); - - cy.get("@datePicker") - .shadow() - .find("ui5-calendar") - .as("calendar") - .realPress(["Shift", "F4"]) .realPress("F4"); cy.get("@calendar") @@ -535,7 +540,11 @@ describe("Date Picker Tests", () => { .shadow() .find("ui5-calendar") .as("calendar") - .realPress("F4") + .realPress("F4"); + + cy.wait(50); + + cy.get("@calendar") .realPress(["Shift", "F4"]); cy.get("@calendar") @@ -1669,17 +1678,28 @@ describe("Legacy date customization and Islamic calendar type", () => { }); it("Islamic calendar type input value", () => { - cy.mount(); + cy.mount( + <> + + + + ); - cy.get("[ui5-date-picker]") - .as("datePicker") - .ui5DatePickerGetInnerInput() - .as("input") - .realClick() - .should("be.focused") - .realType("Rab. I 6, 1440 AH") - .realPress("Enter"); + cy.get("[ui5-input]") // get the value from the input + .invoke("attr", "value") + .then((value) => { + cy.get("[ui5-date-picker]") // manually enter the value from the input to the date picker + .as("datePicker") + .ui5DatePickerGetInnerInput() + .as("input") + .realClick() + .should("be.focused") + .realPress(["Control", "a"]) // clear existing content + .realType(value as string, { delay: 25 }) // type the value + .realPress("Enter"); + }); + cy.wait("Rab. I 6, 1440 AH".length * 26); // the delay from above +1ms buffer cy.get("@datePicker") .should("have.value", "Rab. I 6, 1440 AH"); diff --git a/packages/main/cypress/specs/DateRangePicker.cy.tsx b/packages/main/cypress/specs/DateRangePicker.cy.tsx index 75f74db3d53a..ce0920dabbdc 100644 --- a/packages/main/cypress/specs/DateRangePicker.cy.tsx +++ b/packages/main/cypress/specs/DateRangePicker.cy.tsx @@ -610,30 +610,40 @@ describe("DateRangePicker general interaction", () => { cy.mount(); - // TODO: Remove when focus is applied on month, day, year picker in their onAfterRendering method. It takes the focus one they are rendered even if not visible - cy.wait(500); - cy.get("[ui5-daterange-picker]") .as("dateRangePicker") .shadow() .find("[ui5-datetime-input]") .realClick() - cy.get("@dateRangePicker") - .should("be.focused"); - - cy.realType("09.2024 - 11.2024"); - - cy.realPress("Enter"); - - cy.get("@dateRangePicker") - .should("have.value", "09.2024 - 11.2024") - cy.realPress("F4"); cy.get("@dateRangePicker") .ui5DateRangePickerExpectToBeOpen() + // click on September + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .shadow() + .find("[ui5-monthpicker]") + .shadow() + .find(".ui5-mp-root .ui5-mp-item") + .eq(8) + .realClick(); + + // click on November + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .shadow() + .find("[ui5-monthpicker]") + .shadow() + .find(".ui5-mp-root .ui5-mp-item") + .eq(10) + .realClick(); + + // Verify the range selection is visible in the picker cy.get("@dateRangePicker") .shadow() .find("[ui5-calendar]") @@ -674,6 +684,25 @@ describe("DateRangePicker general interaction", () => { .find("[ui5-yearpicker]") .should("exist") .and("be.visible"); + + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .realPress("F4"); + + cy.wait(100); + + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .realPress(["Shift", "F4"]); + + cy.wait(100); + + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .should("exist"); }); it("Select year range in YearPicker", () => { @@ -681,28 +710,57 @@ describe("DateRangePicker general interaction", () => { .then(api => { return api.setLanguage("en"); }) - + cy.mount(); - + cy.get("[ui5-daterange-picker]") .as("dateRangePicker") .shadow() .find("[ui5-datetime-input]") .realClick() .should("be.focused"); - - cy.realType("0001 - 0006"); - - cy.realPress("Enter"); - - cy.get("[ui5-daterange-picker]") - .should("have.value", "0001 - 0006") - + + // Open picker first cy.realPress("F4"); - + cy.get("@dateRangePicker") .ui5DateRangePickerExpectToBeOpen() - + + // Navigate to year view using keyboard navigation + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .realPress("F4"); // Navigate to month view + cy.wait(100); + + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .realPress(["Shift", "F4"]); // Navigate to year view + cy.wait(300); + + // Select year range by clicking (different years than test 2) + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find(".ui5-yp-root .ui5-yp-item") + .first() // Select first year as start + .realClick(); + + cy.get("@dateRangePicker") + .shadow() + .find("[ui5-calendar]") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find(".ui5-yp-root .ui5-yp-item") + .eq(3) // Select fourth year as end (creating a range) + .realClick(); + + // Validate the same visual behavior as test 2 cy.get("@dateRangePicker") .shadow() .find("[ui5-calendar]") @@ -712,12 +770,19 @@ describe("DateRangePicker general interaction", () => { .find(".ui5-yp-root .ui5-yp-item") .should(years => { const startSelectionYear = years[0]; - const yearInBetween = years[4]; - const endSelectionYear = years[5]; + const yearInBetween1 = years[1]; + const yearInBetween2 = years[2]; + const endSelectionYear = years[3]; expect(startSelectionYear).to.have.class("ui5-yp-item--selected"); - expect(yearInBetween).to.have.class("ui5-yp-item--selected-between"); + expect(yearInBetween1).to.have.class("ui5-yp-item--selected-between"); + expect(yearInBetween2).to.have.class("ui5-yp-item--selected-between"); expect(endSelectionYear).to.have.class("ui5-yp-item--selected"); }); + + // Verify the component has a value + cy.get("@dateRangePicker") + .should("have.attr", "value") + .and("not.be.empty"); }); }); diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index 915044df3ee2..d05a35883296 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -527,40 +527,40 @@ class Calendar extends CalendarPart { /** * The user clicked the "month" button in the header */ - async onHeaderShowMonthPress() { - await this.showMonth(); + onHeaderShowMonthPress() { + this.showMonth(); this.fireDecoratorEvent("show-month-view"); } - async showMonth() { + showMonth() { this._currentPicker = "month"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); // the same as just calling this method without await, but more explicit. } /** * The user clicked the "year" button in the header */ - async onHeaderShowYearPress() { - await this.showYear(); + onHeaderShowYearPress() { + this.showYear(); this.fireDecoratorEvent("show-year-view"); } - async showYear() { + showYear() { this._currentPicker = "year"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } /** * The user clicked the "year range" button in the YearPicker header */ - async onHeaderShowYearRangePress() { - await this.showYearRange(); + onHeaderShowYearRangePress() { + this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } - async showYearRange() { + showYearRange() { this._currentPicker = "yearrange"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } get _currentPickerDOM() { @@ -571,15 +571,15 @@ class Calendar extends CalendarPart { /** * The year clicked the "Previous" button in the header */ - async onHeaderPreviousPress() { - await this._currentPickerDOM._showPreviousPage(); + onHeaderPreviousPress() { + this._currentPickerDOM._showPreviousPage(); } /** * The year clicked the "Next" button in the header */ - async onHeaderNextPress() { - await this._currentPickerDOM._showNextPage(); + onHeaderNextPress() { + this._currentPickerDOM._showNextPage(); } _setSecondaryCalendarTypeButtonText() { @@ -705,62 +705,60 @@ class Calendar extends CalendarPart { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - async onSelectedMonthChange(e: CustomEvent) { + onSelectedMonthChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - async onSelectedYearChange(e: CustomEvent) { + onSelectedYearChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) { this._currentPicker = "month"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - async onSelectedYearRangeChange(e: CustomEvent) { + onSelectedYearRangeChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; this._currentPicker = "year"; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } - async onNavigate(e: CustomEvent) { + onNavigate(e: CustomEvent) { this.timestamp = e.detail.timestamp; - await this._focusCurrentPicker(); + this._focusCurrentPicker(); } _onkeydown(e: KeyboardEvent) { - if (isF4(e) && this._currentPicker !== "month") { - this._currentPicker = "month"; - this.fireDecoratorEvent("show-month-view"); + if (isF4Shift(e)) { + if (this._currentPicker !== "year") { + this._currentPicker = "year"; + this.fireDecoratorEvent("show-year-view"); + } else { + this._currentPicker = "yearrange"; + this.fireDecoratorEvent("show-year-range-view"); + } this._focusCurrentPicker(); - } - - if (!isF4Shift(e)) { return; } - if (this._currentPicker !== "year") { - this._currentPicker = "year"; - this.fireDecoratorEvent("show-year-view"); - } else { - this._currentPicker = "yearrange"; - this.fireDecoratorEvent("show-year-range-view"); + if (isF4(e) && this._currentPicker !== "month") { + this._currentPicker = "month"; + this.fireDecoratorEvent("show-month-view"); + this._focusCurrentPicker(); } - - this._focusCurrentPicker(); } _onLegendFocusOut() { @@ -805,57 +803,57 @@ class Calendar extends CalendarPart { return secondMonthButtonText; } - async onMonthButtonKeyDown(e: KeyboardEvent) { + onMonthButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - await this.showMonth(); + this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - async onMonthButtonKeyUp(e: KeyboardEvent) { + onMonthButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); - await this.showMonth(); + this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - async onYearButtonKeyDown(e: KeyboardEvent) { + onYearButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - await this.showYear(); + this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - async onYearButtonKeyUp(e: KeyboardEvent) { + onYearButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - await this.showYear(); + this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - async onYearRangeButtonKeyDown(e: KeyboardEvent) { + onYearRangeButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - await this.showYearRange(); + this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } - async onYearRangeButtonKeyUp(e: KeyboardEvent) { + onYearRangeButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - await this.showYearRange(); + this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index bf622a2a16d6..8c468c230b14 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -366,7 +366,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - async _modifyTimestampBy(amount: number, preserveDate?: boolean) { + _modifyTimestampBy(amount: number, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "month", preserveDate); this._updateSecondTimestamp(); @@ -374,7 +374,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - await this._focusCorrectMonth(); + this._focusCorrectMonth(); } _onkeyup(e: KeyboardEvent) { @@ -438,8 +438,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - async _showPreviousPage() { - await this._modifyTimestampBy(-PAGE_SIZE, true); + _showPreviousPage() { + this._modifyTimestampBy(-PAGE_SIZE, true); } /** @@ -447,8 +447,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - async _showNextPage() { - await this._modifyTimestampBy(PAGE_SIZE, true); + _showNextPage() { + this._modifyTimestampBy(PAGE_SIZE, true); } _isOutOfSelectableRange(date: CalendarDate, minDate: CalendarDate, maxDate: CalendarDate): boolean { diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index 946dce567a1e..f5459eb86e26 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -362,7 +362,7 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * @param amount * @private */ - async _modifyTimestampBy(amount: number) { + _modifyTimestampBy(amount: number) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "year"); this._updateSecondTimestamp(); @@ -370,8 +370,7 @@ class YearPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - await renderFinished(); - this._focusableYear.focus(); + this._focusCorrectYear(); } _onkeyup(e: KeyboardEvent) { @@ -434,9 +433,9 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - async _showPreviousPage() { + _showPreviousPage() { const pageSize = this._getPageSize(); - await this._modifyTimestampBy(-pageSize); + this._modifyTimestampBy(-pageSize); } /** @@ -444,8 +443,8 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - async _showNextPage() { - await this._modifyTimestampBy(this._getPageSize()); + _showNextPage() { + this._modifyTimestampBy(this._getPageSize()); } } From fe80356f24b47f9ae517e968995c8d6a228d6ca3 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Fri, 22 Aug 2025 08:23:14 +0300 Subject: [PATCH 8/9] Revert "fix: stabilize tests by reordering them" This reverts commit edd1519efa2ff8e597b0f3b73353678334d1e644. --- packages/main/cypress/specs/Calendar.cy.tsx | 308 +++++++++--------- packages/main/cypress/specs/DatePicker.cy.tsx | 134 ++++---- .../main/cypress/specs/DateRangePicker.cy.tsx | 121 ++----- packages/main/src/Calendar.ts | 100 +++--- packages/main/src/MonthPicker.ts | 12 +- packages/main/src/YearPicker.ts | 13 +- 6 files changed, 300 insertions(+), 388 deletions(-) diff --git a/packages/main/cypress/specs/Calendar.cy.tsx b/packages/main/cypress/specs/Calendar.cy.tsx index 282c62e669a3..f438ca2034b2 100644 --- a/packages/main/cypress/specs/Calendar.cy.tsx +++ b/packages/main/cypress/specs/Calendar.cy.tsx @@ -36,67 +36,46 @@ const getCalendarsWithWeekNumbers = () => (<> ); describe("Calendar general interaction", () => { - it("Focus goes into the current day item of the day picker –– UNSTABLE", () => { - const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); - cy.mount(getDefaultCalendar(date)); - - cy.ui5CalendarGetDay("#calendar1", "974851200") - .as("selectedDay"); - - cy.get("@selectedDay") - .realClick(); - - cy.get("@selectedDay") - .should("have.focus") - .realPress("Tab", { pressDelay: 1 }); - - cy.get("#calendar1") - .shadow() - .find(".ui5-calheader") - .as("calheader"); - - cy.get("@calheader") - .find("[data-ui5-cal-header-btn-month]") - .as("monthBtn"); - - cy.get("@monthBtn") - .should("have.focus") - .realPress("Tab", { pressDelay: 1 }); - - cy.get("@calheader") - .find("[data-ui5-cal-header-btn-year]") - .as("yearBtn"); - - cy.get("@yearBtn") - .should("have.focus") - .realPress(["Shift", "Tab"], { pressDelay: 1 }); - - cy.get("@monthBtn") - .should("have.focus") - .realPress(["Shift", "Tab"], { pressDelay: 1 }); + it("Focus goes into the current day item of the day picker", () => { + const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); + cy.mount(getDefaultCalendar(date)); - cy.get("@selectedDay") - .should("have.focus"); - }); + cy.ui5CalendarGetDay("#calendar1", "974851200") + .as("selectedDay"); - it("Focus goes into first selected day of the range selection –– UNSTABLE", () => { - cy.mount( - - - ); + cy.get("@selectedDay") + .realClick(); - const timestamp = new Date(Date.UTC(2021, 0, 20, 0, 0, 0)).valueOf() / 1000; // 1611100800 + cy.get("@selectedDay") + .should("have.focus") + .realPress("Tab"); cy.get("#calendar1") .shadow() .find(".ui5-calheader") + .as("calheader"); + + cy.get("@calheader") .find("[data-ui5-cal-header-btn-month]") - .realClick(); + .as("monthBtn"); + + cy.get("@monthBtn") + .should("have.focus") + .realPress("Tab"); - cy.ui5CalendarGetMonth("#calendar1", timestamp.toString()) - .realClick(); + cy.get("@calheader") + .find("[data-ui5-cal-header-btn-year]") + .as("yearBtn"); + + cy.get("@yearBtn") + .should("have.focus") + .realPress(["Shift", "Tab"]); - cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) + cy.get("@monthBtn") + .should("have.focus") + .realPress(["Shift", "Tab"]); + + cy.get("@selectedDay") .should("have.focus"); }); @@ -150,6 +129,34 @@ describe("Calendar general interaction", () => { }); }); + it("Calendar focuses the selected year when yearpicker is opened with space", () => { + const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); + cy.mount(getDefaultCalendar(date)); + + cy.ui5CalendarGetDay("#calendar1", "974851200") + .realClick(); + + cy.focused().realPress("Tab"); + cy.focused().realPress("Tab"); + cy.focused().realPress("Space"); + + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .should("not.have.attr", "hidden"); + + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find("[tabindex='0']") + .invoke("attr", "data-sap-timestamp") + .then(_timestamp => { + const focusedYear = new Date(parseInt(_timestamp!) * 1000).getUTCFullYear(); + expect(focusedYear).to.equal(2000); + }); + }); + it("Should focus the selected range when YearRange Picker is opened", () => { const YEAR = 1997; const date = Date.UTC(YEAR); @@ -279,8 +286,8 @@ describe("Calendar general interaction", () => { }); - it("Page up/down increments/decrements the year range in the year picker – UNSTABLE", () => { - const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); + it("Page up/down increments/decrements the month value", () => { + const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") @@ -290,13 +297,12 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); - cy.focused().realPress(["Shift", "F4"]); cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1980, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -304,34 +310,39 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); }); }); - - it("Navigation with Page down decrements the year ranges in the year range picker –– UNSTABLE", () => { - const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); + it("Shift + Page up/down increments/decrements the year value by one", () => { + const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .ui5CalendarShowYearRangePicker(); + .shadow() + .find("[ui5-daypicker]") + .shadow() + .find("[tabindex='0']") + .realClick(); + + cy.focused().realPress(["Shift", "PageUp"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 10, 1, 0, 0, 0))); }); - cy.focused().realPress("PageUp"); + cy.focused().realPress(["Shift", "PageDown"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1838, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); }); }); - it("Page up/down increments/decrements the month value", () => { + it("Ctrl + Shift + Page up/down increments/decrements the year value by ten", () => { const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); @@ -342,15 +353,15 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); - cy.focused().realPress("PageUp"); + cy.focused().realPress(["Control", "Shift", "PageUp"]); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1990, 10, 1, 0, 0, 0))); }); - cy.focused().realPress("PageDown"); + cy.focused().realPress(["Control", "Shift", "PageDown"]); cy.get("#calendar1") .invoke("prop", "timestamp") @@ -359,17 +370,24 @@ describe("Calendar general interaction", () => { }); }); - it("Navigation with Page up increments the year ranges in the year range picker –– UNSTABLE", () => { - const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); + it("Page up/down increments/decrements the year value in the month picker", () => { + const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .ui5CalendarShowYearRangePicker(); + .shadow() + .find("[ui5-daypicker]") + .shadow() + .find("[tabindex='0']") + .realClick(); + + cy.focused().realPress("F4"); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 9, 1, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -377,12 +395,12 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2158, 9, 16, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); }); - it("Shift + Page up/down increments/decrements the year value by one", () => { - const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); + it("Page up/down increments/decrements the year range in the year picker", () => { + const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") @@ -392,69 +410,57 @@ describe("Calendar general interaction", () => { .find("[tabindex='0']") .realClick(); - cy.focused().realPress(["Shift", "PageUp"]); + cy.focused().realPress(["Shift", "F4"]); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1980, 9, 1, 0, 0, 0))); }); - cy.focused().realPress(["Shift", "PageDown"]); + cy.focused().realPress("PageDown"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); }); }); - it("Ctrl + Shift + Page up/down increments/decrements the year value by ten", () => { - const date = new Date(Date.UTC(2000, 10, 1, 0, 0, 0)); + it("Navigation with Page down decrements the year ranges in the year range picker", () => { + const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .shadow() - .find("[ui5-daypicker]") - .shadow() - .find("[tabindex='0']") - .realClick(); - - cy.focused().realPress(["Control", "Shift", "PageUp"]); + .ui5CalendarShowYearRangePicker(); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1990, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); - cy.focused().realPress(["Control", "Shift", "PageDown"]); + cy.focused().realPress("PageUp"); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 10, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1838, 9, 16, 0, 0, 0))); }); }); - it("Page up/down increments/decrements the year value in the month picker", () => { - const date = new Date(Date.UTC(2000, 9, 1, 0, 0, 0)); + it("Navigation with Page up increments the year ranges in the year range picker", () => { + const date = new Date(Date.UTC(1998, 9, 16, 0, 0, 0)); cy.mount(getDefaultCalendar(date)); cy.get("#calendar1") - .shadow() - .find("[ui5-daypicker]") - .shadow() - .find("[tabindex='0']") - .realClick(); - - cy.focused().realPress("F4"); - cy.focused().realPress("PageUp"); + .ui5CalendarShowYearRangePicker(); cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1999, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); cy.focused().realPress("PageDown"); @@ -462,7 +468,7 @@ describe("Calendar general interaction", () => { cy.get("#calendar1") .invoke("prop", "timestamp") .then(_timestamp => { - expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2000, 9, 1, 0, 0, 0))); + expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(2158, 9, 16, 0, 0, 0))); }); }); @@ -489,6 +495,27 @@ describe("Calendar general interaction", () => { }); }); + it("Keyboard navigation works properly, when calendar selection type is set to 'Multiple'", () => { + cy.mount(); + + const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; + + cy.get("#calendar1").invoke("prop", "timestamp", timestamp); + + cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) + .focus(); + + // Select the focused date + cy.focused().realPress("Space"); + + // Deselect the focused date + cy.focused().realPress("Space"); + cy.focused().realPress("ArrowRight"); + + cy.ui5CalendarGetDay("#calendar1", "971222400") + .should("have.focus"); + }); + it("Calendar with 'Range' selection type", () => { cy.mount(); const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; @@ -519,27 +546,6 @@ describe("Calendar general interaction", () => { }); }); - it("Keyboard navigation works properly, when calendar selection type is set to 'Multiple' –– UNSTABLE", () => { - cy.mount(); - - const timestamp = new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000; - - cy.get("#calendar1").invoke("prop", "timestamp", timestamp); - - cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) - .realClick(); - - // Select the focused date - cy.focused().realPress("Space"); - - // Deselect the focused date - cy.focused().realPress("Space"); - cy.focused().realPress("ArrowRight"); - - cy.ui5CalendarGetDay("#calendar1", "971222400") - .should("have.focus"); - }); - it("Previous and next buttons are disabled when necessary", () => { cy.mount(<> @@ -711,7 +717,7 @@ describe("Calendar general interaction", () => { expect(spans[0]).to.have.text("1414 AH"); expect(spans[1]).to.have.text("1993 - 1994"); }); - }); + }); it("Calendar renders secondary type for Year Range Picker", () => { cy.mount( @@ -797,35 +803,6 @@ describe("Calendar general interaction", () => { .should("not.have.class", "ui5-yp-item--disabled"); }); - - it("Calendar focuses the selected year when yearpicker is opened with space –– UNSTABLE", () => { - const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0)); - cy.mount(getDefaultCalendar(date)); - - cy.ui5CalendarGetDay("#calendar1", "974851200") - .realClick(); - - cy.focused().realPress("Tab", { pressDelay: 1 }); - cy.focused().realPress("Tab", { pressDelay: 1 }); - cy.focused().realPress("Space", { pressDelay: 1 }); - - cy.get("#calendar1") - .shadow() - .find("[ui5-yearpicker]") - .should("not.have.attr", "hidden"); - - cy.get("#calendar1") - .shadow() - .find("[ui5-yearpicker]") - .shadow() - .find("[tabindex='0']") - .invoke("attr", "data-sap-timestamp") - .then(_timestamp => { - const focusedYear = new Date(parseInt(_timestamp!) * 1000).getUTCFullYear(); - expect(focusedYear).to.equal(2000); - }); - }); - it("Year Ranges outside of the min and max dates are disabled", () => { cy.mount( { .should("have.class", "ui5-yrp-item--disabled"); }); + it("Focus goes into first selected day of the range selection", () => { + cy.mount( + + + ); + + const timestamp = new Date(Date.UTC(2021, 0, 20, 0, 0, 0)).valueOf() / 1000; // 1611100800 + + cy.get("#calendar1") + .shadow() + .find(".ui5-calheader") + .find("[data-ui5-cal-header-btn-month]") + .realClick(); + + cy.ui5CalendarGetMonth("#calendar1", timestamp.toString()) + .realClick(); + + cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) + .should("have.focus"); + }); + it("Special date respects format-pattern given to the calendar", () => { cy.mount( @@ -959,7 +957,3 @@ describe("Calendar general interaction", () => { .should("have.text", "Sun"); }); }); - -describe("Calendar - UNSTABLE TESTS", () => { - -}); diff --git a/packages/main/cypress/specs/DatePicker.cy.tsx b/packages/main/cypress/specs/DatePicker.cy.tsx index d6344170b22d..ee3bd557aee6 100644 --- a/packages/main/cypress/specs/DatePicker.cy.tsx +++ b/packages/main/cypress/specs/DatePicker.cy.tsx @@ -3,7 +3,6 @@ import "../../src/Assets.js"; import { setLanguage } from "@ui5/webcomponents-base/dist/config/Language.js"; import DatePicker from "../../src/DatePicker.js"; import Label from "../../src/Label.js"; -import Input from "../../src/Input.js"; describe("Date Picker Tests", () => { it("input renders", () => { @@ -269,35 +268,6 @@ describe("Date Picker Tests", () => { .should("have.value", ""); }); - it("change fires when we change the input back to its original value", () => { - cy.mount(); - - cy.get("[ui5-date-picker]") - .as("datePicker") - .ui5DatePickerGetInnerInput() - .as("input") - .realClick() - .should("be.focused"); - - cy.get("@input") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Backspace") - .realPress("Enter"); - - cy.get("@datePicker") - .should("have.value", ""); - - cy.get("@input") - .should("be.focused") - .realType("2015") - .realPress("Enter"); - - cy.get("@datePicker") - .should("have.value", "2015"); - }); - it("respect first day of the week - monday", () => { cy.wrap({ setLanguage }) .invoke("setLanguage", "bg"); @@ -356,6 +326,35 @@ describe("Date Picker Tests", () => { .should("have.attr", "open"); }); + it("change fires when we change the input back to its original value", () => { + cy.mount(); + + cy.get("[ui5-date-picker]") + .as("datePicker") + .ui5DatePickerGetInnerInput() + .as("input") + .realClick() + .should("be.focused"); + + cy.get("@input") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Backspace") + .realPress("Enter"); + + cy.get("@datePicker") + .should("have.value", ""); + + cy.get("@input") + .should("be.focused") + .realType("2015") + .realPress("Enter"); + + cy.get("@datePicker") + .should("have.value", "2015"); + }); + it("change fires every time tomorrow is typed and normalized", () => { cy.mount(); @@ -393,25 +392,6 @@ describe("Date Picker Tests", () => { .should("have.class", "ui5-dp-item--selected"); }); - it("[Shift] + [F4] shows year picker after date picker is open", () => { - cy.mount(); - - cy.get("[ui5-date-picker]") - .as("datePicker") - .ui5DatePickerValueHelpIconPress(); - - cy.get("@datePicker") - .shadow() - .find("ui5-calendar") - .as("calendar") - .realPress(["Shift", "F4"]); - - cy.get("@calendar") - .shadow() - .find("ui5-yearpicker") - .should("be.visible"); - }); - it("[F4] toggles the calendar", () => { cy.mount(); @@ -505,15 +485,13 @@ describe("Date Picker Tests", () => { .should("be.visible"); }); - it("[F4] shows month picker after year picker is open", () => { + it("[Shift] + [F4] shows year picker after date picker is open", () => { cy.mount(); cy.get("[ui5-date-picker]") .as("datePicker") .ui5DatePickerValueHelpIconPress(); - cy.wait(150) - cy.get("@datePicker") .shadow() .find("ui5-calendar") @@ -521,6 +499,23 @@ describe("Date Picker Tests", () => { .realPress(["Shift", "F4"]); cy.get("@calendar") + .shadow() + .find("ui5-yearpicker") + .should("be.visible"); + }); + + it("[F4] shows month picker after year picker is open", () => { + cy.mount(); + + cy.get("[ui5-date-picker]") + .as("datePicker") + .ui5DatePickerValueHelpIconPress(); + + cy.get("@datePicker") + .shadow() + .find("ui5-calendar") + .as("calendar") + .realPress(["Shift", "F4"]) .realPress("F4"); cy.get("@calendar") @@ -540,11 +535,7 @@ describe("Date Picker Tests", () => { .shadow() .find("ui5-calendar") .as("calendar") - .realPress("F4"); - - cy.wait(50); - - cy.get("@calendar") + .realPress("F4") .realPress(["Shift", "F4"]); cy.get("@calendar") @@ -1678,28 +1669,17 @@ describe("Legacy date customization and Islamic calendar type", () => { }); it("Islamic calendar type input value", () => { - cy.mount( - <> - - - - ); + cy.mount(); - cy.get("[ui5-input]") // get the value from the input - .invoke("attr", "value") - .then((value) => { - cy.get("[ui5-date-picker]") // manually enter the value from the input to the date picker - .as("datePicker") - .ui5DatePickerGetInnerInput() - .as("input") - .realClick() - .should("be.focused") - .realPress(["Control", "a"]) // clear existing content - .realType(value as string, { delay: 25 }) // type the value - .realPress("Enter"); - }); + cy.get("[ui5-date-picker]") + .as("datePicker") + .ui5DatePickerGetInnerInput() + .as("input") + .realClick() + .should("be.focused") + .realType("Rab. I 6, 1440 AH") + .realPress("Enter"); - cy.wait("Rab. I 6, 1440 AH".length * 26); // the delay from above +1ms buffer cy.get("@datePicker") .should("have.value", "Rab. I 6, 1440 AH"); diff --git a/packages/main/cypress/specs/DateRangePicker.cy.tsx b/packages/main/cypress/specs/DateRangePicker.cy.tsx index ce0920dabbdc..75f74db3d53a 100644 --- a/packages/main/cypress/specs/DateRangePicker.cy.tsx +++ b/packages/main/cypress/specs/DateRangePicker.cy.tsx @@ -610,40 +610,30 @@ describe("DateRangePicker general interaction", () => { cy.mount(); + // TODO: Remove when focus is applied on month, day, year picker in their onAfterRendering method. It takes the focus one they are rendered even if not visible + cy.wait(500); + cy.get("[ui5-daterange-picker]") .as("dateRangePicker") .shadow() .find("[ui5-datetime-input]") .realClick() - cy.realPress("F4"); + cy.get("@dateRangePicker") + .should("be.focused"); - cy.get("@dateRangePicker") - .ui5DateRangePickerExpectToBeOpen() + cy.realType("09.2024 - 11.2024"); - // click on September - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .shadow() - .find("[ui5-monthpicker]") - .shadow() - .find(".ui5-mp-root .ui5-mp-item") - .eq(8) - .realClick(); + cy.realPress("Enter"); + + cy.get("@dateRangePicker") + .should("have.value", "09.2024 - 11.2024") + + cy.realPress("F4"); - // click on November cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .shadow() - .find("[ui5-monthpicker]") - .shadow() - .find(".ui5-mp-root .ui5-mp-item") - .eq(10) - .realClick(); + .ui5DateRangePickerExpectToBeOpen() - // Verify the range selection is visible in the picker cy.get("@dateRangePicker") .shadow() .find("[ui5-calendar]") @@ -684,25 +674,6 @@ describe("DateRangePicker general interaction", () => { .find("[ui5-yearpicker]") .should("exist") .and("be.visible"); - - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .realPress("F4"); - - cy.wait(100); - - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .realPress(["Shift", "F4"]); - - cy.wait(100); - - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .should("exist"); }); it("Select year range in YearPicker", () => { @@ -710,57 +681,28 @@ describe("DateRangePicker general interaction", () => { .then(api => { return api.setLanguage("en"); }) - + cy.mount(); - + cy.get("[ui5-daterange-picker]") .as("dateRangePicker") .shadow() .find("[ui5-datetime-input]") .realClick() .should("be.focused"); - - // Open picker first + + cy.realType("0001 - 0006"); + + cy.realPress("Enter"); + + cy.get("[ui5-daterange-picker]") + .should("have.value", "0001 - 0006") + cy.realPress("F4"); - + cy.get("@dateRangePicker") .ui5DateRangePickerExpectToBeOpen() - - // Navigate to year view using keyboard navigation - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .realPress("F4"); // Navigate to month view - cy.wait(100); - - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .realPress(["Shift", "F4"]); // Navigate to year view - cy.wait(300); - - // Select year range by clicking (different years than test 2) - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .shadow() - .find("[ui5-yearpicker]") - .shadow() - .find(".ui5-yp-root .ui5-yp-item") - .first() // Select first year as start - .realClick(); - - cy.get("@dateRangePicker") - .shadow() - .find("[ui5-calendar]") - .shadow() - .find("[ui5-yearpicker]") - .shadow() - .find(".ui5-yp-root .ui5-yp-item") - .eq(3) // Select fourth year as end (creating a range) - .realClick(); - - // Validate the same visual behavior as test 2 + cy.get("@dateRangePicker") .shadow() .find("[ui5-calendar]") @@ -770,19 +712,12 @@ describe("DateRangePicker general interaction", () => { .find(".ui5-yp-root .ui5-yp-item") .should(years => { const startSelectionYear = years[0]; - const yearInBetween1 = years[1]; - const yearInBetween2 = years[2]; - const endSelectionYear = years[3]; + const yearInBetween = years[4]; + const endSelectionYear = years[5]; expect(startSelectionYear).to.have.class("ui5-yp-item--selected"); - expect(yearInBetween1).to.have.class("ui5-yp-item--selected-between"); - expect(yearInBetween2).to.have.class("ui5-yp-item--selected-between"); + expect(yearInBetween).to.have.class("ui5-yp-item--selected-between"); expect(endSelectionYear).to.have.class("ui5-yp-item--selected"); }); - - // Verify the component has a value - cy.get("@dateRangePicker") - .should("have.attr", "value") - .and("not.be.empty"); }); }); diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index bfac15ec38c9..866961eaf8fe 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -529,40 +529,40 @@ class Calendar extends CalendarPart { /** * The user clicked the "month" button in the header */ - onHeaderShowMonthPress() { - this.showMonth(); + async onHeaderShowMonthPress() { + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } - showMonth() { + async showMonth() { this._currentPicker = "month"; - this._focusCurrentPicker(); // the same as just calling this method without await, but more explicit. + await this._focusCurrentPicker(); } /** * The user clicked the "year" button in the header */ - onHeaderShowYearPress() { - this.showYear(); + async onHeaderShowYearPress() { + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } - showYear() { + async showYear() { this._currentPicker = "year"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } /** * The user clicked the "year range" button in the YearPicker header */ - onHeaderShowYearRangePress() { - this.showYearRange(); + async onHeaderShowYearRangePress() { + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } - showYearRange() { + async showYearRange() { this._currentPicker = "yearrange"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } get _currentPickerDOM() { @@ -573,15 +573,15 @@ class Calendar extends CalendarPart { /** * The year clicked the "Previous" button in the header */ - onHeaderPreviousPress() { - this._currentPickerDOM._showPreviousPage(); + async onHeaderPreviousPress() { + await this._currentPickerDOM._showPreviousPage(); } /** * The year clicked the "Next" button in the header */ - onHeaderNextPress() { - this._currentPickerDOM._showNextPage(); + async onHeaderNextPress() { + await this._currentPickerDOM._showNextPage(); } _setSecondaryCalendarTypeButtonText() { @@ -707,60 +707,62 @@ class Calendar extends CalendarPart { this._fireEventAndUpdateSelectedDates(e.detail.dates); } - onSelectedMonthChange(e: CustomEvent) { + async onSelectedMonthChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - onSelectedYearChange(e: CustomEvent) { + async onSelectedYearChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) { this._currentPicker = "day"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) { this._currentPicker = "month"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } else { this._fireEventAndUpdateSelectedDates(e.detail.dates); } } - onSelectedYearRangeChange(e: CustomEvent) { + async onSelectedYearRangeChange(e: CustomEvent) { this.timestamp = e.detail.timestamp; this._currentPicker = "year"; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } - onNavigate(e: CustomEvent) { + async onNavigate(e: CustomEvent) { this.timestamp = e.detail.timestamp; - this._focusCurrentPicker(); + await this._focusCurrentPicker(); } _onkeydown(e: KeyboardEvent) { - if (isF4Shift(e)) { - if (this._currentPicker !== "year") { - this._currentPicker = "year"; - this.fireDecoratorEvent("show-year-view"); - } else { - this._currentPicker = "yearrange"; - this.fireDecoratorEvent("show-year-range-view"); - } - this._focusCurrentPicker(); - return; - } - if (isF4(e) && this._currentPicker !== "month") { this._currentPicker = "month"; this.fireDecoratorEvent("show-month-view"); this._focusCurrentPicker(); } + + if (!isF4Shift(e)) { + return; + } + + if (this._currentPicker !== "year") { + this._currentPicker = "year"; + this.fireDecoratorEvent("show-year-view"); + } else { + this._currentPicker = "yearrange"; + this.fireDecoratorEvent("show-year-range-view"); + } + + this._focusCurrentPicker(); } _onLegendFocusOut() { @@ -805,57 +807,57 @@ class Calendar extends CalendarPart { return secondMonthButtonText; } - onMonthButtonKeyDown(e: KeyboardEvent) { + async onMonthButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showMonth(); + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - onMonthButtonKeyUp(e: KeyboardEvent) { + async onMonthButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); - this.showMonth(); + await this.showMonth(); this.fireDecoratorEvent("show-month-view"); } } - onYearButtonKeyDown(e: KeyboardEvent) { + async onYearButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showYear(); + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - onYearButtonKeyUp(e: KeyboardEvent) { + async onYearButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - this.showYear(); + await this.showYear(); this.fireDecoratorEvent("show-year-view"); } } - onYearRangeButtonKeyDown(e: KeyboardEvent) { + async onYearRangeButtonKeyDown(e: KeyboardEvent) { if (isSpace(e)) { e.preventDefault(); } if (isEnter(e)) { - this.showYearRange(); + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } - onYearRangeButtonKeyUp(e: KeyboardEvent) { + async onYearRangeButtonKeyUp(e: KeyboardEvent) { if (isSpace(e)) { - this.showYearRange(); + await this.showYearRange(); this.fireDecoratorEvent("show-year-range-view"); } } diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index 8c468c230b14..bf622a2a16d6 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -366,7 +366,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * @param preserveDate whether to preserve the day of the month (f.e. 15th of March + 1 month = 15th of April) * @private */ - _modifyTimestampBy(amount: number, preserveDate?: boolean) { + async _modifyTimestampBy(amount: number, preserveDate?: boolean) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "month", preserveDate); this._updateSecondTimestamp(); @@ -374,7 +374,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - this._focusCorrectMonth(); + await this._focusCorrectMonth(); } _onkeyup(e: KeyboardEvent) { @@ -438,8 +438,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - _showPreviousPage() { - this._modifyTimestampBy(-PAGE_SIZE, true); + async _showPreviousPage() { + await this._modifyTimestampBy(-PAGE_SIZE, true); } /** @@ -447,8 +447,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - _showNextPage() { - this._modifyTimestampBy(PAGE_SIZE, true); + async _showNextPage() { + await this._modifyTimestampBy(PAGE_SIZE, true); } _isOutOfSelectableRange(date: CalendarDate, minDate: CalendarDate, maxDate: CalendarDate): boolean { diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index f5459eb86e26..946dce567a1e 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -362,7 +362,7 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * @param amount * @private */ - _modifyTimestampBy(amount: number) { + async _modifyTimestampBy(amount: number) { // Modify the current timestamp this._safelyModifyTimestampBy(amount, "year"); this._updateSecondTimestamp(); @@ -370,7 +370,8 @@ class YearPicker extends CalendarPart implements ICalendarPicker { // Notify the calendar to update its timestamp this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! }); - this._focusCorrectYear(); + await renderFinished(); + this._focusableYear.focus(); } _onkeyup(e: KeyboardEvent) { @@ -433,9 +434,9 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the "<" button in the calendar header (same as "PageUp") * @protected */ - _showPreviousPage() { + async _showPreviousPage() { const pageSize = this._getPageSize(); - this._modifyTimestampBy(-pageSize); + await this._modifyTimestampBy(-pageSize); } /** @@ -443,8 +444,8 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * **Note:** when the user presses the ">" button in the calendar header (same as "PageDown") * @protected */ - _showNextPage() { - this._modifyTimestampBy(this._getPageSize()); + async _showNextPage() { + await this._modifyTimestampBy(this._getPageSize()); } } From a3197a324c58c4da5520ff696428116eef3808b4 Mon Sep 17 00:00:00 2001 From: Stoyan Date: Fri, 22 Aug 2025 10:12:37 +0300 Subject: [PATCH 9/9] test: wait for focus --- packages/main/cypress/specs/Calendar.cy.tsx | 39 ++++++++++++- packages/main/cypress/specs/DatePicker.cy.tsx | 58 +++++++++++++++++-- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/packages/main/cypress/specs/Calendar.cy.tsx b/packages/main/cypress/specs/Calendar.cy.tsx index f438ca2034b2..c568863f31ae 100644 --- a/packages/main/cypress/specs/Calendar.cy.tsx +++ b/packages/main/cypress/specs/Calendar.cy.tsx @@ -408,9 +408,19 @@ describe("Calendar general interaction", () => { .find("[ui5-daypicker]") .shadow() .find("[tabindex='0']") - .realClick(); + .realClick() + .should("have.focus"); cy.focused().realPress(["Shift", "F4"]); + + // Wait for focus to settle before proceeding + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + cy.focused().realPress("PageUp"); cy.get("#calendar1") @@ -419,6 +429,14 @@ describe("Calendar general interaction", () => { expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1980, 9, 1, 0, 0, 0))); }); + // Wait for focus to settle before proceeding + cy.get("#calendar1") + .shadow() + .find("[ui5-yearpicker]") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + cy.focused().realPress("PageDown"); cy.get("#calendar1") @@ -441,6 +459,14 @@ describe("Calendar general interaction", () => { expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); + // Wait for focus to settle before proceeding + cy.get("#calendar1") + .shadow() + .find("[ui5-yearrangepicker]") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + cy.focused().realPress("PageUp"); cy.get("#calendar1") @@ -463,6 +489,14 @@ describe("Calendar general interaction", () => { expect(new Date(_timestamp * 1000)).to.deep.equal(new Date(Date.UTC(1998, 9, 16, 0, 0, 0))); }); + // Wait for focus to settle before proceeding + cy.get("#calendar1") + .shadow() + .find("[ui5-yearrangepicker]") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + cy.focused().realPress("PageDown"); cy.get("#calendar1") @@ -503,7 +537,8 @@ describe("Calendar general interaction", () => { cy.get("#calendar1").invoke("prop", "timestamp", timestamp); cy.ui5CalendarGetDay("#calendar1", timestamp.toString()) - .focus(); + .focus() + .should("have.focus"); // Select the focused date cy.focused().realPress("Space"); diff --git a/packages/main/cypress/specs/DatePicker.cy.tsx b/packages/main/cypress/specs/DatePicker.cy.tsx index ee3bd557aee6..11b961bae94a 100644 --- a/packages/main/cypress/specs/DatePicker.cy.tsx +++ b/packages/main/cypress/specs/DatePicker.cy.tsx @@ -511,12 +511,35 @@ describe("Date Picker Tests", () => { .as("datePicker") .ui5DatePickerValueHelpIconPress(); + // Focus the day picker's focusable element first cy.get("@datePicker") .shadow() .find("ui5-calendar") .as("calendar") - .realPress(["Shift", "F4"]) - .realPress("F4"); + .shadow() + .find("ui5-daypicker") + .shadow() + .find("[tabindex='0']") + .should("be.visible") + .focus() + .should("have.focus"); + + cy.focused().realPress(["Shift", "F4"]); + + // Wait for year picker to be visible and focused + cy.get("@calendar") + .shadow() + .find("ui5-yearpicker") + .should("be.visible"); + + cy.get("@calendar") + .shadow() + .find("ui5-yearpicker") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + + cy.focused().realPress("F4"); cy.get("@calendar") .shadow() @@ -531,12 +554,35 @@ describe("Date Picker Tests", () => { .as("datePicker") .ui5DatePickerValueHelpIconPress(); + // Focus the day picker's focusable element first cy.get("@datePicker") .shadow() .find("ui5-calendar") .as("calendar") - .realPress("F4") - .realPress(["Shift", "F4"]); + .shadow() + .find("ui5-daypicker") + .shadow() + .find("[tabindex='0']") + .should("be.visible") + .focus() + .should("have.focus"); + + cy.focused().realPress("F4"); + + // Wait for month picker to be visible and focused + cy.get("@calendar") + .shadow() + .find("ui5-monthpicker") + .should("be.visible"); + + cy.get("@calendar") + .shadow() + .find("ui5-monthpicker") + .shadow() + .find("[tabindex='0']") + .should("have.focus"); + + cy.focused().realPress(["Shift", "F4"]); cy.get("@calendar") .shadow() @@ -1676,7 +1722,9 @@ describe("Legacy date customization and Islamic calendar type", () => { .ui5DatePickerGetInnerInput() .as("input") .realClick() - .should("be.focused") + .should("be.focused"); + + cy.get("@input") .realType("Rab. I 6, 1440 AH") .realPress("Enter");