Skip to content

Commit e74a9ca

Browse files
committed
fix(ui5-calendar): remove auto-focus logic in onAfterRendering() hook
1 parent 12866b3 commit e74a9ca

File tree

8 files changed

+93
-72
lines changed

8 files changed

+93
-72
lines changed

packages/main/src/Calendar.ts

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,11 @@ import CalendarHeaderCss from "./generated/themes/CalendarHeader.css.js";
4949
import { CALENDAR_HEADER_NEXT_BUTTON, CALENDAR_HEADER_PREVIOUS_BUTTON } from "./generated/i18n/i18n-defaults.js";
5050
import type { YearRangePickerChangeEventDetail } from "./YearRangePicker.js";
5151

52-
interface ICalendarPicker {
53-
_showPreviousPage: () => void,
54-
_showNextPage: () => void,
52+
interface ICalendarPicker extends HTMLElement {
53+
_showPreviousPage: () => void | Promise<void>,
54+
_showNextPage: () => void | Promise<void>,
5555
_hasPreviousPage: () => boolean,
5656
_hasNextPage: () => boolean,
57-
_autoFocus?: boolean,
5857
_currentYearRange?: CalendarYearRangeT,
5958
}
6059

@@ -335,6 +334,15 @@ class Calendar extends CalendarPart {
335334
this._valueIsProcessed = false;
336335
}
337336

337+
async _focusCurrentPicker() {
338+
await renderFinished();
339+
this._currentPickerDOM.focus();
340+
}
341+
342+
async _onfocusin() {
343+
await this._focusCurrentPicker();
344+
}
345+
338346
/**
339347
* @private
340348
*/
@@ -465,7 +473,6 @@ class Calendar extends CalendarPart {
465473
if (defaultTypes.includes(this._selectedItemType)) {
466474
this._selectedItemType = "None"; // In order to avoid filtering of default types
467475
}
468-
this._currentPickerDOM._autoFocus = false;
469476
}
470477

471478
/**
@@ -524,40 +531,40 @@ class Calendar extends CalendarPart {
524531
/**
525532
* The user clicked the "month" button in the header
526533
*/
527-
onHeaderShowMonthPress() {
528-
this.showMonth();
534+
async onHeaderShowMonthPress() {
535+
await this.showMonth();
529536
this.fireDecoratorEvent("show-month-view");
530537
}
531538

532-
showMonth() {
533-
this._currentPickerDOM._autoFocus = false;
539+
async showMonth() {
534540
this._currentPicker = "month";
541+
await this._focusCurrentPicker();
535542
}
536543

537544
/**
538545
* The user clicked the "year" button in the header
539546
*/
540-
onHeaderShowYearPress() {
541-
this.showYear();
547+
async onHeaderShowYearPress() {
548+
await this.showYear();
542549
this.fireDecoratorEvent("show-year-view");
543550
}
544551

545-
showYear() {
546-
this._currentPickerDOM._autoFocus = false;
552+
async showYear() {
547553
this._currentPicker = "year";
554+
await this._focusCurrentPicker();
548555
}
549556

550557
/**
551558
* The user clicked the "year range" button in the YearPicker header
552559
*/
553-
onHeaderShowYearRangePress() {
554-
this.showYearRange();
560+
async onHeaderShowYearRangePress() {
561+
await this.showYearRange();
555562
this.fireDecoratorEvent("show-year-range-view");
556563
}
557564

558-
showYearRange() {
559-
this._currentPickerDOM._autoFocus = false;
565+
async showYearRange() {
560566
this._currentPicker = "yearrange";
567+
await this._focusCurrentPicker();
561568
}
562569

563570
get _currentPickerDOM() {
@@ -570,21 +577,13 @@ class Calendar extends CalendarPart {
570577
*/
571578
onHeaderPreviousPress() {
572579
this._currentPickerDOM._showPreviousPage();
573-
574-
if (this.calendarLegend) {
575-
this._currentPickerDOM._autoFocus = true;
576-
}
577580
}
578581

579582
/**
580583
* The year clicked the "Next" button in the header
581584
*/
582585
onHeaderNextPress() {
583586
this._currentPickerDOM._showNextPage();
584-
585-
if (this.calendarLegend) {
586-
this._currentPickerDOM._autoFocus = true;
587-
}
588587
}
589588

590589
_setSecondaryCalendarTypeButtonText() {
@@ -710,40 +709,40 @@ class Calendar extends CalendarPart {
710709
this._fireEventAndUpdateSelectedDates(e.detail.dates);
711710
}
712711

713-
onSelectedMonthChange(e: CustomEvent<MonthPickerChangeEventDetail>) {
712+
async onSelectedMonthChange(e: CustomEvent<MonthPickerChangeEventDetail>) {
714713
this.timestamp = e.detail.timestamp;
715714

716715
if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) {
717716
this._currentPicker = "day";
717+
await this._focusCurrentPicker();
718718
} else {
719719
this._fireEventAndUpdateSelectedDates(e.detail.dates);
720720
}
721-
722-
this._currentPickerDOM._autoFocus = true;
723721
}
724722

725-
onSelectedYearChange(e: CustomEvent<YearPickerChangeEventDetail>) {
723+
async onSelectedYearChange(e: CustomEvent<YearPickerChangeEventDetail>) {
726724
this.timestamp = e.detail.timestamp;
727725

728726
if (this._pickersMode === CalendarPickersMode.DAY_MONTH_YEAR) {
729727
this._currentPicker = "day";
728+
await this._focusCurrentPicker();
730729
} else if (this._pickersMode === CalendarPickersMode.MONTH_YEAR) {
731730
this._currentPicker = "month";
731+
await this._focusCurrentPicker();
732732
} else {
733733
this._fireEventAndUpdateSelectedDates(e.detail.dates);
734734
}
735-
736-
this._currentPickerDOM._autoFocus = true;
737735
}
738736

739-
onSelectedYearRangeChange(e: CustomEvent<YearRangePickerChangeEventDetail>) {
737+
async onSelectedYearRangeChange(e: CustomEvent<YearRangePickerChangeEventDetail>) {
740738
this.timestamp = e.detail.timestamp;
741739
this._currentPicker = "year";
742-
this._currentPickerDOM._autoFocus = true;
740+
await this._focusCurrentPicker();
743741
}
744742

745743
onNavigate(e: CustomEvent) {
746744
this.timestamp = e.detail.timestamp;
745+
this._focusCurrentPicker();
747746
}
748747

749748
_onkeydown(e: KeyboardEvent) {

packages/main/src/CalendarTemplate.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default function CalendarTemplate(this: Calendar) {
1212
<div
1313
class="ui5-cal-root"
1414
onKeyDown={this._onkeydown}
15+
onFocusIn={this._onfocusin}
1516
>
1617
<div id={`${this._id}-content`} class="ui5-cal-content">
1718
<DayPicker

packages/main/src/DayPicker.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type LocaleData from "@ui5/webcomponents-localization/dist/LocaleData.js"
88
import getCachedLocaleDataInstance from "@ui5/webcomponents-localization/dist/getCachedLocaleDataInstance.js";
99
import InvisibleMessageMode from "@ui5/webcomponents-base/dist/types/InvisibleMessageMode.js";
1010
import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js";
11+
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
1112
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
1213
import {
1314
isSpace,
@@ -195,8 +196,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
195196
@query("[data-sap-focus-ref]")
196197
_focusableDay!: HTMLElement;
197198

198-
_autoFocus?: boolean;
199-
200199
@i18n("@ui5/webcomponents")
201200
static i18nBundle: I18nBundle;
202201

@@ -404,12 +403,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
404403
return dayNames.some(dayName => dayName.length > 4);
405404
}
406405

407-
onAfterRendering() {
408-
if (this._autoFocus && !this._hidden) {
409-
this.focus();
410-
}
411-
}
412-
413406
_focusCorrectDay() {
414407
if (this._shouldFocusDay) {
415408
this._focusableDay.focus();
@@ -420,15 +413,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
420413
return document.activeElement !== this._focusableDay && this._specialCalendarDates.length === 0;
421414
}
422415

423-
_onfocusin() {
424-
this._autoFocus = true;
425-
this._focusCorrectDay();
426-
}
427-
428-
_onfocusout() {
429-
this._autoFocus = false;
430-
}
431-
432416
/**
433417
* Tells if the day is selected (dark blue).
434418
* @param timestamp
@@ -618,6 +602,21 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
618602
}
619603
}
620604

605+
/**
606+
* Sets the focus reference to the day that was clicked with mousedown.
607+
* @param e
608+
* @private
609+
*/
610+
_onmousedown(e: MouseEvent) {
611+
const target = e.target as HTMLElement;
612+
const clickedItem = target.closest(".ui5-dp-item") as HTMLElement;
613+
614+
if (clickedItem && this._isDayPressed(clickedItem)) {
615+
const timestamp = this._getTimestampFromDom(clickedItem);
616+
this._setTimestamp(timestamp);
617+
}
618+
}
619+
621620
_onkeydown(e: KeyboardEvent) {
622621
let preventDefault = true;
623622

@@ -723,16 +722,20 @@ class DayPicker extends CalendarPart implements ICalendarPicker {
723722
* Called by the Calendar component.
724723
* @protected
725724
*/
726-
_showPreviousPage() {
725+
async _showPreviousPage() {
727726
this._modifyTimestampBy(-1, "month", false);
727+
await renderFinished();
728+
this._focusCorrectDay();
728729
}
729730

730731
/**
731732
* Called by the Calendar component.
732733
* @protected
733734
*/
734-
_showNextPage() {
735+
async _showNextPage() {
735736
this._modifyTimestampBy(1, "month", false);
737+
await renderFinished();
738+
this._focusCorrectDay();
736739
}
737740

738741
/**

packages/main/src/DayPickerTemplate.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ export default function DayPickerTemplate(this: DayPicker) {
1414
onKeyDown={this._onkeydown}
1515
onKeyUp={this._onkeyup}
1616
onClick={this._onclick}
17+
onMouseDown={this._onmousedown}
1718
onMouseOver={this._onmouseover}
18-
onFocusIn={this._onfocusin}
19-
onFocusOut={this._onfocusout}
2019
>
2120
<div id={`${this._id}-content`} class="ui5-dp-content" role="grid" aria-roledescription={this.ariaRoledescription}>
2221
<div role="row" class="ui5-dp-days-names-container">

packages/main/src/MonthPicker.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
22
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
3+
import query from "@ui5/webcomponents-base/dist/decorators/query.js";
34
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
45
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
56
import getCachedLocaleDataInstance from "@ui5/webcomponents-localization/dist/getCachedLocaleDataInstance.js";
@@ -35,6 +36,7 @@ import MonthPickerTemplate from "./MonthPickerTemplate.js";
3536
// Styles
3637
import monthPickerStyles from "./generated/themes/MonthPicker.css.js";
3738
import CalendarSelectionMode from "./types/CalendarSelectionMode.js";
39+
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
3840

3941
const isBetween = (x: number, num1: number, num2: number) => x > Math.min(num1, num2) && x < Math.max(num1, num2);
4042
const PAGE_SIZE = 12; // total months on a single page
@@ -132,6 +134,9 @@ class MonthPicker extends CalendarPart implements ICalendarPicker {
132134
@property({ type: Number })
133135
_secondTimestamp?: number;
134136

137+
@query("[data-sap-focus-ref]")
138+
_focusableMonth!: HTMLElement;
139+
135140
@i18n("@ui5/webcomponents")
136141
static i18nBundle: I18nBundle;
137142

@@ -143,17 +148,26 @@ class MonthPicker extends CalendarPart implements ICalendarPicker {
143148
this._buildMonths();
144149
}
145150

146-
onAfterRendering() {
147-
if (!this._hidden) {
148-
this.focus();
149-
}
150-
}
151-
152151
get rowSize() {
153152
return (this.secondaryCalendarType === CalendarType.Islamic && this.primaryCalendarType !== CalendarType.Islamic)
154153
|| (this.secondaryCalendarType === CalendarType.Persian && this.primaryCalendarType !== CalendarType.Persian) ? 2 : 3;
155154
}
156155

156+
_focusCorrectMonth() {
157+
if (this._shouldFocusMonth) {
158+
this._focusableMonth.focus();
159+
}
160+
}
161+
162+
get _shouldFocusMonth() {
163+
return document.activeElement !== this._focusableMonth;
164+
}
165+
166+
async _onfocusin() {
167+
await renderFinished();
168+
this._focusCorrectMonth();
169+
}
170+
157171
_buildMonths() {
158172
if (this._hidden) {
159173
return;
@@ -330,6 +344,21 @@ class MonthPicker extends CalendarPart implements ICalendarPicker {
330344
}
331345
}
332346

347+
/**
348+
* Sets the focus reference to the month that was clicked with mousedown.
349+
* @param e
350+
* @private
351+
*/
352+
_onmousedown(e: MouseEvent) {
353+
const target = e.target as HTMLElement;
354+
const clickedItem = target.closest(".ui5-mp-item") as HTMLElement;
355+
356+
if (clickedItem) {
357+
const timestamp = this._getTimestampFromDom(clickedItem);
358+
this._setTimestamp(timestamp);
359+
}
360+
}
361+
333362
/**
334363
* Modifies timestamp by a given amount of months and,
335364
* if necessary, loads the prev/next page.

packages/main/src/MonthPickerTemplate.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export default function MonthPickerTemplate(this: MonthPicker) {
1313
onKeyDown={this._onkeydown}
1414
onKeyUp={this._onkeyup}
1515
onClick={this._selectMonth}
16+
onMouseDown={this._onmousedown}
17+
onFocusIn={this._onfocusin}
1618
>
1719
{this._monthsInterval.map(months =>
1820
<div role="row" class="ui5-mp-quarter">

packages/main/src/YearPicker.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,6 @@ class YearPicker extends CalendarPart implements ICalendarPicker {
240240
this._yearsInterval = intervals;
241241
}
242242

243-
onAfterRendering() {
244-
if (!this._hidden) {
245-
this.focus();
246-
}
247-
}
248-
249243
/**
250244
* Returns true if year timestamp is inside the selection range.
251245
* @private

packages/main/src/YearRangePicker.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -313,12 +313,6 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker {
313313
return isBetweenInclusive(timestamp, this.selectedDates[0], this.selectedDates[1]);
314314
}
315315

316-
onAfterRendering() {
317-
if (!this._hidden) {
318-
this.focus();
319-
}
320-
}
321-
322316
_onkeydown(e: KeyboardEvent) {
323317
let preventDefault = true;
324318
const pageSize = this._getPageSize();

0 commit comments

Comments
 (0)