From 3e1fdff91437607a168f7b4fee9e3bc6c0bc044b Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sat, 7 Dec 2024 18:13:45 +0200 Subject: [PATCH 1/7] fix(ui5-calendar): add tooltips to special dates --- packages/main/src/Calendar.hbs | 1 + packages/main/src/DayPicker.hbs | 1 + packages/main/src/DayPicker.ts | 29 +++++++++++++++++++++++--- packages/main/src/themes/DayPicker.css | 1 + 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/main/src/Calendar.hbs b/packages/main/src/Calendar.hbs index 798ab443e7fd..f6ff68b934a5 100644 --- a/packages/main/src/Calendar.hbs +++ b/packages/main/src/Calendar.hbs @@ -9,6 +9,7 @@ format-pattern="{{_formatPattern}}" .selectedDates="{{_selectedDatesTimestamps}}" .specialCalendarDates="{{_specialCalendarDates}}" + ._calendarLegend="{{calendarLegend}}" ._hidden="{{_isDayPickerHidden}}" .primaryCalendarType="{{_primaryCalendarType}}" .secondaryCalendarType="{{_secondaryCalendarType}}" diff --git a/packages/main/src/DayPicker.hbs b/packages/main/src/DayPicker.hbs index 1dfbc877f2d9..51842fbb191c 100644 --- a/packages/main/src/DayPicker.hbs +++ b/packages/main/src/DayPicker.hbs @@ -32,6 +32,7 @@ ?data-sap-focus-ref="{{this.focusRef}}" data-sap-timestamp="{{this.timestamp}}" role="gridcell" + title="{{this.tooltip}}" aria-selected="{{this.ariaSelected}}" aria-label="{{this.ariaLabel}}" aria-disabled="{{this.ariaDisabled}}" diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 6e320b358d3e..9767bce2042a 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -30,6 +30,7 @@ import { isPageDownAlt, isPageDownShiftCtrl, } from "@ui5/webcomponents-base/dist/Keys.js"; +import CalendarLegend from "./CalendarLegend.js"; import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js"; import CalendarType from "@ui5/webcomponents-base/dist/types/CalendarType.js"; import UI5Date from "@ui5/webcomponents-localization/dist/dates/UI5Date.js"; @@ -72,6 +73,7 @@ type Day = { selected: boolean, _isSecondaryCalendarType: boolean, classes: string, + tooltip?: string, ariaLabel: string, ariaSelected: string, ariaDisabled: string | undefined, @@ -111,6 +113,7 @@ type DayPickerNavigateEventDetail = { tag: "ui5-daypicker", styles: dayPickerCSS, template: DayPickerTemplate, + dependencies: [CalendarLegend], }) /** * Fired when the selected date(s) change @@ -168,6 +171,9 @@ class DayPicker extends CalendarPart implements ICalendarPicker { @property({ type: Array }) _dayNames: Array = []; + @property({ type: Array }) + _calendarLegend!: Array; + /** * When set, the component will skip all work in onBeforeRendering and will not automatically set the focus on itself * @private @@ -218,6 +224,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { const secondaryMonthsNames = this.hasSecondaryCalendarType ? localeData.getMonths("wide", this.secondaryCalendarType) as Array : []; const nonWorkingDayLabel = DayPicker.i18nBundle.getText(DAY_PICKER_NON_WORKING_DAY); const todayLabel = DayPicker.i18nBundle.getText(DAY_PICKER_TODAY); + const calendarLegend = this._calendarLegend[0]; const tempDate = this._getFirstDay(); // date that will be changed by 1 day 42 times const todayDate = CalendarDate.fromLocalJSDate(UI5Date.getInstance(), this._primaryCalendarType); // current day date - calculate once const calendarDate = this._calendarDate; // store the _calendarDate value as this getter is expensive and degrades IE11 perf @@ -237,6 +244,8 @@ class DayPicker extends CalendarPart implements ICalendarPicker { const specialCalendarDate = specialCalendarDates.find(specialDate => specialDate.specialDateTimestamp === timestamp); const specialDayType = specialCalendarDate ? specialCalendarDate.type : ""; + const calendarLegendItemText = calendarLegend?.items.find(item => item.type === specialDayType)?.text; + const unnamedCalendarTypeLabel = calendarLegendItemText && !this._isDefaultCalendarLegendType(specialDayType) ? calendarLegendItemText : ""; const isFocused = tempDate.getMonth() === calendarDate.getMonth() && tempDate.getDate() === calendarDate.getDate(); const isSelected = this._isDaySelected(timestamp); @@ -247,16 +256,20 @@ class DayPicker extends CalendarPart implements ICalendarPicker { const isToday = tempDate.isSame(todayDate); const isFirstDayOfWeek = tempDate.getDay() === firstDayOfWeek; - const nonWorkingAriaLabel = isWeekend ? `${nonWorkingDayLabel} ` : ""; + const nonWorkingAriaLabel = (isWeekend || specialDayType === "NonWorking") && specialDayType !== "Working" + ? `${nonWorkingDayLabel} ` + : ""; const todayAriaLabel = isToday ? `${todayLabel} ` : ""; const tempSecondDateNumber = tempSecondDate ? tempSecondDate.getDate() : ""; const tempSecondYearNumber = tempSecondDate ? tempSecondDate.getYear() : ""; const secondaryMonthsNamesString = secondaryMonthsNames.length > 0 ? secondaryMonthsNames[tempSecondDate!.getMonth()] : ""; + const tooltip = `${todayAriaLabel}${nonWorkingAriaLabel}${unnamedCalendarTypeLabel}`; + const ariaLabel = this.hasSecondaryCalendarType - ? `${todayAriaLabel}${nonWorkingAriaLabel}${monthsNames[tempDate.getMonth()]} ${tempDate.getDate()}, ${tempDate.getYear()}; ${secondaryMonthsNamesString} ${tempSecondDateNumber}, ${tempSecondYearNumber}` - : `${todayAriaLabel}${nonWorkingAriaLabel}${monthsNames[tempDate.getMonth()]} ${tempDate.getDate()}, ${tempDate.getYear()}`; + ? `${monthsNames[tempDate.getMonth()]} ${tempDate.getDate()}, ${tempDate.getYear()}; ${secondaryMonthsNamesString} ${tempSecondDateNumber}, ${tempSecondYearNumber} ${tooltip} ` + : `${monthsNames[tempDate.getMonth()]} ${tempDate.getDate()}, ${tempDate.getYear()} ${tooltip}`; const day: Day = { timestamp: timestamp.toString(), @@ -267,6 +280,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { secondDay: this.hasSecondaryCalendarType ? (tempSecondDate as CalendarDate).getDate() : undefined, _isSecondaryCalendarType: this.hasSecondaryCalendarType, classes: `ui5-dp-item ui5-dp-wday${dayOfTheWeek}`, + tooltip, ariaLabel, ariaSelected: String(isSelected || isSelectedBetween), ariaDisabled: isDisabled || isOtherMonth ? "true" : undefined, @@ -792,6 +806,15 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return (target.className.indexOf("ui5-dp-item") > -1) || (targetParent && targetParent.classList && targetParent.classList.contains("ui5-dp-item")); } + _isDefaultCalendarLegendType(type: string): boolean { + return type === "NonWorking" || type === "Working" || type === "Today" || type === "Selected" || type === "None"; + } + + _getCalendarLegendItemText(type: string): string { + const calendarLegendItem = this._calendarLegend[0]?.items.find(item => item.type === type); + return calendarLegendItem?.text || ""; + } + _getSecondaryDay(tempDate: CalendarDate): CalendarDate { return new CalendarDate(tempDate, this.secondaryCalendarType); } diff --git a/packages/main/src/themes/DayPicker.css b/packages/main/src/themes/DayPicker.css index f0017b93c235..d5f992f1583b 100644 --- a/packages/main/src/themes/DayPicker.css +++ b/packages/main/src/themes/DayPicker.css @@ -87,6 +87,7 @@ font-size: var(--sapFontSize); border: var(--_ui5_daypicker_item_border); border-radius: var(--_ui5_daypicker_item_border_radius); + cursor: pointer; } .ui5-dp-item:hover { From e68b07634fdca0b114aed6151aadebc06089ce3e Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sat, 7 Dec 2024 18:29:41 +0200 Subject: [PATCH 2/7] fix: remove redudant leftover code --- packages/main/src/DayPicker.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 9767bce2042a..3b8448f21a3b 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -113,7 +113,6 @@ type DayPickerNavigateEventDetail = { tag: "ui5-daypicker", styles: dayPickerCSS, template: DayPickerTemplate, - dependencies: [CalendarLegend], }) /** * Fired when the selected date(s) change @@ -810,11 +809,6 @@ class DayPicker extends CalendarPart implements ICalendarPicker { return type === "NonWorking" || type === "Working" || type === "Today" || type === "Selected" || type === "None"; } - _getCalendarLegendItemText(type: string): string { - const calendarLegendItem = this._calendarLegend[0]?.items.find(item => item.type === type); - return calendarLegendItem?.text || ""; - } - _getSecondaryDay(tempDate: CalendarDate): CalendarDate { return new CalendarDate(tempDate, this.secondaryCalendarType); } From 927316dc31a206e492ecfa9df790c9668e6853ab Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sat, 7 Dec 2024 18:39:39 +0200 Subject: [PATCH 3/7] fix: adjust according to a test case --- packages/main/src/DayPicker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 3b8448f21a3b..574a6e5cdf93 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -223,7 +223,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { const secondaryMonthsNames = this.hasSecondaryCalendarType ? localeData.getMonths("wide", this.secondaryCalendarType) as Array : []; const nonWorkingDayLabel = DayPicker.i18nBundle.getText(DAY_PICKER_NON_WORKING_DAY); const todayLabel = DayPicker.i18nBundle.getText(DAY_PICKER_TODAY); - const calendarLegend = this._calendarLegend[0]; + const calendarLegend = this._calendarLegend && this._calendarLegend[0]; const tempDate = this._getFirstDay(); // date that will be changed by 1 day 42 times const todayDate = CalendarDate.fromLocalJSDate(UI5Date.getInstance(), this._primaryCalendarType); // current day date - calculate once const calendarDate = this._calendarDate; // store the _calendarDate value as this getter is expensive and degrades IE11 perf From 8ce84d705ab4cad4d45bc6488465f9312fccb3e1 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sat, 7 Dec 2024 18:51:49 +0200 Subject: [PATCH 4/7] fix: clear lint errors --- packages/main/src/DayPicker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 574a6e5cdf93..959be2b9e604 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -30,7 +30,6 @@ import { isPageDownAlt, isPageDownShiftCtrl, } from "@ui5/webcomponents-base/dist/Keys.js"; -import CalendarLegend from "./CalendarLegend.js"; import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js"; import CalendarType from "@ui5/webcomponents-base/dist/types/CalendarType.js"; import UI5Date from "@ui5/webcomponents-localization/dist/dates/UI5Date.js"; @@ -38,6 +37,7 @@ import CalendarUtils from "@ui5/webcomponents-localization/dist/CalendarUtils.js import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js"; import CalendarSelectionMode from "./types/CalendarSelectionMode.js"; import CalendarPart from "./CalendarPart.js"; +import type CalendarLegend from "./CalendarLegend.js"; import type { ICalendarPicker, SpecialCalendarDateT, From a76d30ac20306be3f107b3854af893f9217131dd Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Tue, 17 Dec 2024 17:41:16 +0200 Subject: [PATCH 5/7] fix: adjust internal method --- packages/main/src/DayPicker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 4b09152d7123..196cf8b12f42 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -793,7 +793,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { } _isDefaultCalendarLegendType(type: string): boolean { - return type === "NonWorking" || type === "Working" || type === "Today" || type === "Selected" || type === "None"; + return ["NonWorking", "Working", "Today", "Selected", "None"].includes(type); } _getSecondaryDay(tempDate: CalendarDate): CalendarDate { From 5e9f6e0f2c4710679fc86e5e500042db2019d08f Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Tue, 17 Dec 2024 17:57:19 +0200 Subject: [PATCH 6/7] fix: update tooltips on legend item change --- packages/main/src/Calendar.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index 748506d79814..a8df94a8bae3 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -1,6 +1,6 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import type UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; -import type { ChangeInfo } from "@ui5/webcomponents-base/dist/UI5Element.js"; +import type { ChangeInfo, InvalidationInfo } from "@ui5/webcomponents-base/dist/UI5Element.js"; import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; @@ -315,6 +315,8 @@ class Calendar extends CalendarPart { @property() _selectedItemType: `${CalendarLegendItemType}` = "None"; + _onCalendarLegendInvalidateBound: (invalidationInfo: InvalidationInfo) => void; + @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; @@ -322,8 +324,16 @@ class Calendar extends CalendarPart { super(); this._valueIsProcessed = false; + this._onCalendarLegendInvalidateBound = this._onCalendarLegendInvalidate.bind(this); } + _onCalendarLegendInvalidate(invalidationInfo: InvalidationInfo) { + if (invalidationInfo.reason === "childchange") { + this._specialCalendarDates; + invalidationInfo.target.detachInvalidate(this._onCalendarLegendInvalidateBound); + } + }; + /** * @private */ @@ -472,6 +482,11 @@ class Calendar extends CalendarPart { onBeforeRendering() { this._normalizeCurrentPicker(); + const calendarLegend = this.calendarLegend.length ? this.calendarLegend[0] : undefined; + + if (calendarLegend) { + calendarLegend.attachInvalidate(this._onCalendarLegendInvalidateBound) + } if (!this._valueIsProcessed) { if (this._selectedDatesTimestamps) { From e8216f1787a0a086f8b830d084b20d396f9774b9 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Tue, 17 Dec 2024 18:07:59 +0200 Subject: [PATCH 7/7] fix: correct lint errors --- packages/main/src/Calendar.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/src/Calendar.ts b/packages/main/src/Calendar.ts index a8df94a8bae3..82ac9bd267a6 100644 --- a/packages/main/src/Calendar.ts +++ b/packages/main/src/Calendar.ts @@ -332,7 +332,7 @@ class Calendar extends CalendarPart { this._specialCalendarDates; invalidationInfo.target.detachInvalidate(this._onCalendarLegendInvalidateBound); } - }; + } /** * @private @@ -485,7 +485,7 @@ class Calendar extends CalendarPart { const calendarLegend = this.calendarLegend.length ? this.calendarLegend[0] : undefined; if (calendarLegend) { - calendarLegend.attachInvalidate(this._onCalendarLegendInvalidateBound) + calendarLegend.attachInvalidate(this._onCalendarLegendInvalidateBound); } if (!this._valueIsProcessed) {