Skip to content

Commit 30d9d2b

Browse files
authoredNov 24, 2020
feat(ui5-date-picker): component is now aligned with the specification (#2304)
Focus handling is aligned with the KBH specification: When an year is selected from the year picker the day picker is shown and focus is set to the last selected date or to the current date. When a month is selected from the month picker the day picker is shown and focus is set to the last selected date or to the current date. When month picker is opened the focus is set to the selected month or to the current month if there are no selected dates. When year picker is opened the focus is set to the selected year or to the current year if there are no selected dates. Selection is applied in compliance with the UX specification, when a picker is opened: Calendar items from the month picker and year picker are not rendered selected, when there are no selected dates. Fixes #2151
1 parent 0cbc0bd commit 30d9d2b

File tree

8 files changed

+134
-67
lines changed

8 files changed

+134
-67
lines changed
 

‎packages/main/src/Calendar.js

+42-25
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,8 @@ class Calendar extends UI5Element {
421421
_handleSelectedDatesChange(event) {
422422
this.selectedDates = [...event.detail.dates];
423423

424+
this.timestamp = this.selectedDates[0];
425+
424426
this.fireEvent("selected-dates-change", { dates: event.detail.dates });
425427
}
426428

@@ -438,21 +440,6 @@ class Calendar extends UI5Element {
438440
}
439441
}
440442

441-
_handleSelectedMonthChange(event) {
442-
const oNewDate = this._calendarDate;
443-
const newMonthIndex = CalendarDate.fromTimestamp(
444-
event.detail.timestamp * 1000,
445-
this._primaryCalendarType
446-
).getMonth();
447-
448-
oNewDate.setMonth(newMonthIndex);
449-
this.timestamp = oNewDate.valueOf() / 1000;
450-
451-
this._hideMonthPicker();
452-
453-
this._focusFirstDayOfMonth(oNewDate);
454-
}
455-
456443
_focusFirstDayOfMonth(targetDate) {
457444
let fistDayOfMonthIndex = -1;
458445

@@ -469,19 +456,35 @@ class Calendar extends UI5Element {
469456
dayPicker._itemNav.focusCurrent();
470457
}
471458

459+
_handleSelectedMonthChange(event) {
460+
const oNewDate = this._calendarDate;
461+
const oFocusedDate = CalendarDate.fromTimestamp(event.detail.timestamp * 1000, this._primaryCalendarType);
462+
463+
oNewDate.setMonth(oFocusedDate.getMonth());
464+
this.timestamp = oNewDate.valueOf() / 1000;
465+
this._monthPicker.timestamp = this.timestamp;
466+
467+
this._hideMonthPicker();
468+
this._setDayPickerCurrentIndex(oNewDate);
469+
}
470+
472471
_handleSelectedYearChange(event) {
473-
const oNewDate = CalendarDate.fromTimestamp(
474-
event.detail.timestamp * 1000,
475-
this._primaryCalendarType
476-
);
477-
oNewDate.setMonth(0);
478-
oNewDate.setDate(1);
472+
const oNewDate = this._calendarDate;
473+
const oFocusedDate = CalendarDate.fromTimestamp(event.detail.timestamp * 1000, this._primaryCalendarType);
479474

475+
oNewDate.setYear(oFocusedDate.getYear());
480476
this.timestamp = oNewDate.valueOf() / 1000;
477+
this._yearPicker.timestamp = this.timestamp;
481478

482479
this._hideYearPicker();
480+
this._setDayPickerCurrentIndex(oNewDate);
481+
}
483482

484-
this._focusFirstDayOfMonth(oNewDate);
483+
_setDayPickerCurrentIndex(calDate) {
484+
const currentDate = new CalendarDate(calDate);
485+
const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]");
486+
const currentDateIndex = dayPicker._getVisibleDays(currentDate).findIndex(date => date.valueOf() === currentDate.valueOf());
487+
dayPicker._itemNav.currentIndex = currentDateIndex;
485488
}
486489

487490
_handleMonthButtonPress() {
@@ -565,11 +568,9 @@ class Calendar extends UI5Element {
565568
}
566569
});
567570

568-
const weekDaysCount = 7;
569-
570571
if (lastDayOfMonthIndex !== -1) {
571572
// find the DOM for the last day index
572-
const lastDay = dayPicker.shadowRoot.querySelector(".ui5-dp-content").children[parseInt(lastDayOfMonthIndex / weekDaysCount) + 1].children[(lastDayOfMonthIndex % weekDaysCount)];
573+
const lastDay = dayPicker.shadowRoot.querySelectorAll(".ui5-dp-content .ui5-dp-item")[lastDayOfMonthIndex];
573574

574575
// update current item in ItemNavigation
575576
dayPicker._itemNav.current = lastDayOfMonthIndex;
@@ -675,6 +676,14 @@ class Calendar extends UI5Element {
675676

676677
this._calendarWidth = calendarRect.width.toString();
677678
this._calendarHeight = calendarRect.height.toString();
679+
680+
const monthPicker = this.shadowRoot.querySelector("[ui5-monthpicker]");
681+
monthPicker._selectedDates = [...this.selectedDates];
682+
const currentMonthIndex = monthPicker._itemNav._getItems().findIndex(item => {
683+
const calDate = CalendarDate.fromTimestamp(parseInt(item.timestamp) * 1000, this._primaryCalendarType);
684+
return calDate.getMonth() === this._calendarDate.getMonth();
685+
});
686+
monthPicker._itemNav.currentIndex = currentMonthIndex;
678687
this._header._isMonthButtonHidden = true;
679688
}
680689

@@ -691,6 +700,14 @@ class Calendar extends UI5Element {
691700

692701
this._calendarWidth = calendarRect.width.toString();
693702
this._calendarHeight = calendarRect.height.toString();
703+
704+
const yearPicker = this.shadowRoot.querySelector("[ui5-yearpicker]");
705+
yearPicker._selectedDates = [...this.selectedDates];
706+
const currentYearIndex = yearPicker._itemNav._getItems().findIndex(item => {
707+
const calDate = CalendarDate.fromTimestamp(parseInt(item.timestamp) * 1000, this._primaryCalendarType);
708+
return calDate.getYear() === this._calendarDate.getYear();
709+
});
710+
yearPicker._itemNav.currentIndex = currentYearIndex;
694711
}
695712

696713
_hideMonthPicker() {

‎packages/main/src/DatePicker.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { isPhone, isIE } from "@ui5/webcomponents-base/dist/Device.js";
2525
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
2626
import "@ui5/webcomponents-icons/dist/appointment-2.js";
2727
import "@ui5/webcomponents-icons/dist/decline.js";
28+
import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js";
2829
import { DATEPICKER_OPEN_ICON_TITLE, DATEPICKER_DATE_ACC_TEXT, INPUT_SUGGESTIONS_TITLE } from "./generated/i18n/i18n-defaults.js";
2930
import Icon from "./Icon.js";
3031
import Button from "./Button.js";
@@ -419,14 +420,16 @@ class DatePicker extends UI5Element {
419420
calendar._hideYearPicker();
420421
}
421422
},
422-
afterOpen: () => {
423+
afterOpen: async () => {
424+
await RenderScheduler.whenFinished();
423425
const calendar = this.calendar;
424426

425427
if (!calendar) {
426428
return;
427429
}
428430

429431
const dayPicker = calendar.shadowRoot.querySelector(`#${calendar._id}-daypicker`);
432+
dayPicker._inputLiveChangeTrigger = false;
430433
const selectedDay = dayPicker.shadowRoot.querySelector(".ui5-dp-item--selected");
431434
const today = dayPicker.shadowRoot.querySelector(".ui5-dp-item--now");
432435
let focusableDay = selectedDay || today;
@@ -666,6 +669,15 @@ class DatePicker extends UI5Element {
666669
const emptyValue = nextValue === "";
667670
const isValid = emptyValue || this._checkValueValidity(nextValue);
668671

672+
if (this.responsivePopover) {
673+
const calendar = this.calendar;
674+
const dayPicker = calendar.shadowRoot.querySelector(`#${calendar._id}-daypicker`);
675+
// If day picker component rerendering is triggered due to a change in the date picker component input filed,
676+
// then mark this trigger and avoid moving the focus from the date picker input field throughout the on after
677+
// rendering hook of the day picker component
678+
dayPicker._inputLiveChangeTrigger = true;
679+
}
680+
669681
this.value = nextValue;
670682
this.fireEvent("input", { value: nextValue, valid: isValid });
671683
}

‎packages/main/src/DayPicker.js

+5
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,11 @@ class DayPicker extends UI5Element {
389389

390390
onAfterRendering() {
391391
this._fireDayPickerRendered();
392+
if (this._inputLiveChangeTrigger) {
393+
this._inputLiveChangeTrigger = false;
394+
} else {
395+
this._itemNav.focusCurrent();
396+
}
392397
}
393398

394399
_onmousedown(event) {

‎packages/main/src/MonthPicker.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ const metadata = {
6464
type: String,
6565
},
6666

67+
_selectedDates: {
68+
type: Integer,
69+
multiple: true,
70+
},
71+
6772
_quarters: {
6873
type: Object,
6974
multiple: true,
@@ -160,27 +165,31 @@ class MonthPicker extends UI5Element {
160165
ItemNavigation.BORDER_REACH,
161166
this._handleItemNavigationBorderReach.bind(this)
162167
);
168+
169+
this._selectedDates = [];
163170
}
164171

165172
onBeforeRendering() {
166173
const localeData = getCachedLocaleDataInstance(getLocale());
167174

168175
const quarters = [];
169-
const oCalDate = CalendarDate.fromTimestamp(new Date().getTime(), this._primaryCalendarType);
176+
const oCalDate = this._calendarDate;
170177
let timestamp;
171178

179+
/* eslint-disable no-loop-func */
172180
for (let i = 0; i < 12; i++) {
173181
oCalDate.setMonth(i);
174182
timestamp = oCalDate.valueOf() / 1000;
175183

176184
const month = {
177185
timestamp: timestamp.toString(),
178186
id: `${this._id}-m${i}`,
187+
selected: this._selectedDates.some(d => d === timestamp),
179188
name: localeData.getMonths("wide", this._primaryCalendarType)[i],
180189
classes: "ui5-mp-item",
181190
};
182191

183-
if (this._month === i) {
192+
if (month.selected) {
184193
month.classes += " ui5-mp-item--selected";
185194
}
186195

@@ -233,8 +242,8 @@ class MonthPicker extends UI5Element {
233242
_onmousedown(event) {
234243
if (event.target.className.indexOf("ui5-mp-item") > -1) {
235244
const targetTimestamp = this.getTimestampFromDOM(event.target);
236-
const focusedItem = this._itemNav._getItems().find(item => parseInt(item.timestamp) === targetTimestamp);
237-
this._itemNav.currentIndex = this._itemNav._getItems().indexOf(focusedItem);
245+
const focusedItemIndex = this._itemNav._getItems().findIndex(item => parseInt(item.timestamp) === targetTimestamp);
246+
this._itemNav.currentIndex = focusedItemIndex;
238247
this._itemNav.focusCurrent();
239248
}
240249
}

‎packages/main/src/YearPicker.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ const metadata = {
6666
defaultValue: undefined,
6767
},
6868

69+
_selectedDates: {
70+
type: Integer,
71+
multiple: true,
72+
},
73+
6974
_selectedYear: {
7075
type: Integer,
7176
noAttribute: true,
@@ -165,6 +170,7 @@ class YearPicker extends UI5Element {
165170
);
166171

167172
this._yearIntervals = [];
173+
this._selectedDates = [];
168174
}
169175

170176
onBeforeRendering() {
@@ -190,6 +196,7 @@ class YearPicker extends UI5Element {
190196
this._selectedYear = this._year;
191197
}
192198

199+
/* eslint-disable no-loop-func */
193200
for (let i = 0; i < YearPicker._ITEMS_COUNT; i++) {
194201
const intervalIndex = parseInt(i / 4);
195202
if (!intervals[intervalIndex]) {
@@ -203,11 +210,15 @@ class YearPicker extends UI5Element {
203210
const year = {
204211
timestamp: timestamp.toString(),
205212
id: `${this._id}-y${timestamp}`,
213+
selected: this._selectedDates.some(itemTimestamp => {
214+
const date = CalendarDate.fromTimestamp(itemTimestamp * 1000, this._primaryCalendarType);
215+
return date.getYear() === oCalDate.getYear();
216+
}),
206217
year: oYearFormat.format(oCalDate.toLocalJSDate()),
207218
classes: "ui5-yp-item",
208219
};
209220

210-
if (oCalDate.getYear() === this._selectedYear) {
221+
if (year.selected) {
211222
year.classes += " ui5-yp-item--selected";
212223
}
213224

@@ -256,8 +267,8 @@ class YearPicker extends UI5Element {
256267
_onmousedown(event) {
257268
if (event.target.className.indexOf("ui5-yp-item") > -1) {
258269
const targetTimestamp = this.getTimestampFromDom(event.target);
259-
const focusedItem = this._itemNav._getItems().find(item => parseInt(item.timestamp) === targetTimestamp);
260-
this._itemNav.currentIndex = this._itemNav._getItems().indexOf(focusedItem);
270+
const focusedItemIndex = this._itemNav._getItems().findIndex(item => parseInt(item.timestamp) === targetTimestamp);
271+
this._itemNav.currentIndex = focusedItemIndex;
261272
this._itemNav.focusCurrent();
262273
}
263274
}
@@ -413,7 +424,7 @@ class YearPicker extends UI5Element {
413424
}
414425

415426
YearPicker._ITEMS_COUNT = 20;
416-
YearPicker._MIDDLE_ITEM_INDEX = 7;
427+
YearPicker._MIDDLE_ITEM_INDEX = 10;
417428

418429
YearPicker.define();
419430

0 commit comments

Comments
 (0)
Please sign in to comment.