Skip to content

fix(ui5-calendar): remove auto-focus logic in onAfterRendering() hook #12066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions packages/main/src/Calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>,
_showNextPage: () => void | Promise<void>,
_hasPreviousPage: () => boolean,
_hasNextPage: () => boolean,
_autoFocus?: boolean,
_currentYearRange?: CalendarYearRangeT,
}

Expand Down Expand Up @@ -335,6 +334,11 @@ class Calendar extends CalendarPart {
this._valueIsProcessed = false;
}

async _focusCurrentPicker() {
await renderFinished();
this._currentPickerDOM.focus();
}

/**
* @private
*/
Expand Down Expand Up @@ -465,7 +469,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;
}

/**
Expand Down Expand Up @@ -530,8 +533,8 @@ class Calendar extends CalendarPart {
}

showMonth() {
this._currentPickerDOM._autoFocus = false;
this._currentPicker = "month";
this._focusCurrentPicker();
}

/**
Expand All @@ -543,8 +546,8 @@ class Calendar extends CalendarPart {
}

showYear() {
this._currentPickerDOM._autoFocus = false;
this._currentPicker = "year";
this._focusCurrentPicker();
}

/**
Expand All @@ -556,8 +559,8 @@ class Calendar extends CalendarPart {
}

showYearRange() {
this._currentPickerDOM._autoFocus = false;
this._currentPicker = "yearrange";
this._focusCurrentPicker();
}

get _currentPickerDOM() {
Expand All @@ -570,21 +573,13 @@ class Calendar extends CalendarPart {
*/
onHeaderPreviousPress() {
this._currentPickerDOM._showPreviousPage();

if (this.calendarLegend) {
this._currentPickerDOM._autoFocus = true;
}
}

/**
* The year clicked the "Next" button in the header
*/
onHeaderNextPress() {
this._currentPickerDOM._showNextPage();

if (this.calendarLegend) {
this._currentPickerDOM._autoFocus = true;
}
}

_setSecondaryCalendarTypeButtonText() {
Expand Down Expand Up @@ -715,41 +710,42 @@ class Calendar extends CalendarPart {

if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) {
this._currentPicker = "day";
this._focusCurrentPicker();
} else {
this._fireEventAndUpdateSelectedDates(e.detail.dates);
}

this._currentPickerDOM._autoFocus = true;
}

onSelectedYearChange(e: CustomEvent<YearPickerChangeEventDetail>) {
this.timestamp = e.detail.timestamp;

if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) {
this._currentPicker = "day";
this._focusCurrentPicker();
} else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) {
this._currentPicker = "month";
this._focusCurrentPicker();
} else {
this._fireEventAndUpdateSelectedDates(e.detail.dates);
}

this._currentPickerDOM._autoFocus = true;
}

onSelectedYearRangeChange(e: CustomEvent<YearRangePickerChangeEventDetail>) {
this.timestamp = e.detail.timestamp;
this._currentPicker = "year";
this._currentPickerDOM._autoFocus = true;
this._focusCurrentPicker();
}

onNavigate(e: CustomEvent) {
this.timestamp = e.detail.timestamp;
this._focusCurrentPicker();
}

_onkeydown(e: KeyboardEvent) {
if (isF4(e) && this._currentPicker !== "month") {
this._currentPicker = "month";
this.fireDecoratorEvent("show-month-view");
this._focusCurrentPicker();
}

if (!isF4Shift(e)) {
Expand All @@ -763,6 +759,8 @@ class Calendar extends CalendarPart {
this._currentPicker = "yearrange";
this.fireDecoratorEvent("show-year-range-view");
}

this._focusCurrentPicker();
}

_onLegendFocusOut() {
Expand Down
36 changes: 22 additions & 14 deletions packages/main/src/DayPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}
Expand All @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -725,6 +729,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
*/
_showPreviousPage() {
this._modifyTimestampBy(-1, "month", false);
this._focusCorrectDay();
}

/**
Expand All @@ -733,6 +738,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
*/
_showNextPage() {
this._modifyTimestampBy(1, "month", false);
this._focusCorrectDay();
}

/**
Expand All @@ -749,6 +755,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker {

// Notify the calendar to update its timestamp
this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! });

this._focusCorrectDay();
}

/**
Expand Down
3 changes: 1 addition & 2 deletions packages/main/src/DayPickerTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
>
<div id={`${this._id}-content`} class="ui5-dp-content" role="grid" aria-roledescription={this.ariaRoledescription}>
<div role="row" class="ui5-dp-days-names-container">
Expand Down
43 changes: 37 additions & 6 deletions packages/main/src/MonthPicker.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -344,6 +373,8 @@ class MonthPicker extends CalendarPart implements ICalendarPicker {

// Notify the calendar to update its timestamp
this.fireDecoratorEvent("navigate", { timestamp: this.timestamp! });

this._focusCorrectMonth();
}

_onkeyup(e: KeyboardEvent) {
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/MonthPickerTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
<div role="row" class="ui5-mp-quarter">
Expand Down
Loading
Loading