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 07865db24ebb..b0c2997296f2 100644 --- a/packages/main/cypress/specs/DatePicker.cy.tsx +++ b/packages/main/cypress/specs/DatePicker.cy.tsx @@ -512,12 +512,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() @@ -532,12 +555,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() @@ -1664,7 +1710,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"); diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index fbfcba77d49d..866961eaf8fe 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, } @@ -337,6 +336,11 @@ class Calendar extends CalendarPart { this._valueIsProcessed = false; } + async _focusCurrentPicker() { + await renderFinished(); + this._currentPickerDOM.focus(); + } + /** * @private */ @@ -467,7 +471,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; } /** @@ -526,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() { - 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,23 +573,15 @@ class Calendar extends CalendarPart { /** * The year clicked the "Previous" button in the header */ - onHeaderPreviousPress() { - this._currentPickerDOM._showPreviousPage(); - - if (this.calendarLegend) { - this._currentPickerDOM._autoFocus = true; - } + async onHeaderPreviousPress() { + await this._currentPickerDOM._showPreviousPage(); } /** * The year clicked the "Next" button in the header */ - onHeaderNextPress() { - this._currentPickerDOM._showNextPage(); - - if (this.calendarLegend) { - this._currentPickerDOM._autoFocus = true; - } + async onHeaderNextPress() { + await this._currentPickerDOM._showNextPage(); } _setSecondaryCalendarTypeButtonText() { @@ -712,46 +707,47 @@ 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) { + async onNavigate(e: CustomEvent) { this.timestamp = e.detail.timestamp; + await this._focusCurrentPicker(); } _onkeydown(e: KeyboardEvent) { if (isF4(e) && this._currentPicker !== "month") { this._currentPicker = "month"; this.fireDecoratorEvent("show-month-view"); + this._focusCurrentPicker(); } if (!isF4Shift(e)) { @@ -765,6 +761,8 @@ class Calendar extends CalendarPart { this._currentPicker = "yearrange"; this.fireDecoratorEvent("show-year-range-view"); } + + this._focusCurrentPicker(); } _onLegendFocusOut() { @@ -809,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/DayPicker.ts b/packages/main/src/DayPicker.ts index 3c9af1711ae0..26298d705c29 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -8,6 +8,7 @@ import type LocaleData from "@ui5/webcomponents-localization/dist/LocaleData.js" import getCachedLocaleDataInstance from "@ui5/webcomponents-localization/dist/getCachedLocaleDataInstance.js"; import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; +import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { isSpace, @@ -195,8 +196,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker { @query("[data-sap-focus-ref]") _focusableDay!: HTMLElement; - _autoFocus?: boolean; - @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; @@ -404,13 +403,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return dayNames.some(dayName => dayName.length > 4); } - onAfterRendering() { - if (this._autoFocus && !this._hidden) { - this.focus(); - } - } - - _focusCorrectDay() { + async _focusCorrectDay() { + await renderFinished(); if (this._shouldFocusDay) { this._focusableDay.focus(); } @@ -421,14 +415,9 @@ class DayPicker extends CalendarPart implements ICalendarPicker { } _onfocusin() { - this._autoFocus = true; this._focusCorrectDay(); } - _onfocusout() { - this._autoFocus = false; - } - /** * Tells if the day is selected (dark blue). * @param timestamp @@ -618,6 +607,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 +727,16 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Called by the Calendar component. * @protected */ - _showPreviousPage() { - this._modifyTimestampBy(-1, "month", false); + async _showPreviousPage() { + await this._modifyTimestampBy(-1, "month", false); } /** * Called by the Calendar component. * @protected */ - _showNextPage() { - this._modifyTimestampBy(1, "month", false); + async _showNextPage() { + await this._modifyTimestampBy(1, "month", false); } /** @@ -742,13 +746,15 @@ 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 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..bf622a2a16d6 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; } + async _focusCorrectMonth() { + await renderFinished(); + if (this._shouldFocusMonth) { + this._focusableMonth.focus(); + } + } + + get _shouldFocusMonth() { + return document.activeElement !== this._focusableMonth; + } + + _onfocusin() { + 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. @@ -337,13 +366,15 @@ 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 this._focusCorrectMonth(); } _onkeyup(e: KeyboardEvent) { @@ -407,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); } /** @@ -416,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/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..946dce567a1e 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,21 @@ class YearPicker extends CalendarPart implements ICalendarPicker { return YearPicker.i18nBundle.getText(YEAR_PICKER_DESCRIPTION); } + async _focusCorrectYear() { + await renderFinished(); + if (this._shouldFocusYear) { + this._focusableYear.focus(); + } + } + + get _shouldFocusYear() { + return document.activeElement !== this._focusableYear; + } + + _onfocusin() { + this._focusCorrectYear(); + } + onBeforeRendering() { if (this._hidden) { return; @@ -240,12 +260,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 @@ -348,13 +362,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) { @@ -417,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); } /** @@ -427,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 8802ae52eac6..f64bbdc3f0f5 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,21 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { return YearRangePicker.i18nBundle.getText(YEAR_RANGE_PICKER_DESCRIPTION); } + async _focusCorrectYearRange() { + await renderFinished(); + if (this._shouldFocusYearRange) { + this._focusableYearRange.focus(); + } + } + + get _shouldFocusYearRange() { + return document.activeElement !== this._focusableYearRange; + } + + _onfocusin() { + this._focusCorrectYearRange(); + } + onBeforeRendering() { if (this._hidden) { return; @@ -313,12 +333,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(); @@ -468,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); @@ -481,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); @@ -494,13 +508,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) {