Skip to content

Commit

Permalink
feat(ui5-date-picker): component is now aligned with the specification (
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
unazko authored Nov 24, 2020
1 parent 0cbc0bd commit 30d9d2b
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 67 deletions.
67 changes: 42 additions & 25 deletions packages/main/src/Calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ class Calendar extends UI5Element {
_handleSelectedDatesChange(event) {
this.selectedDates = [...event.detail.dates];

this.timestamp = this.selectedDates[0];

this.fireEvent("selected-dates-change", { dates: event.detail.dates });
}

Expand All @@ -438,21 +440,6 @@ class Calendar extends UI5Element {
}
}

_handleSelectedMonthChange(event) {
const oNewDate = this._calendarDate;
const newMonthIndex = CalendarDate.fromTimestamp(
event.detail.timestamp * 1000,
this._primaryCalendarType
).getMonth();

oNewDate.setMonth(newMonthIndex);
this.timestamp = oNewDate.valueOf() / 1000;

this._hideMonthPicker();

this._focusFirstDayOfMonth(oNewDate);
}

_focusFirstDayOfMonth(targetDate) {
let fistDayOfMonthIndex = -1;

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

_handleSelectedMonthChange(event) {
const oNewDate = this._calendarDate;
const oFocusedDate = CalendarDate.fromTimestamp(event.detail.timestamp * 1000, this._primaryCalendarType);

oNewDate.setMonth(oFocusedDate.getMonth());
this.timestamp = oNewDate.valueOf() / 1000;
this._monthPicker.timestamp = this.timestamp;

this._hideMonthPicker();
this._setDayPickerCurrentIndex(oNewDate);
}

_handleSelectedYearChange(event) {
const oNewDate = CalendarDate.fromTimestamp(
event.detail.timestamp * 1000,
this._primaryCalendarType
);
oNewDate.setMonth(0);
oNewDate.setDate(1);
const oNewDate = this._calendarDate;
const oFocusedDate = CalendarDate.fromTimestamp(event.detail.timestamp * 1000, this._primaryCalendarType);

oNewDate.setYear(oFocusedDate.getYear());
this.timestamp = oNewDate.valueOf() / 1000;
this._yearPicker.timestamp = this.timestamp;

this._hideYearPicker();
this._setDayPickerCurrentIndex(oNewDate);
}

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

_handleMonthButtonPress() {
Expand Down Expand Up @@ -565,11 +568,9 @@ class Calendar extends UI5Element {
}
});

const weekDaysCount = 7;

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

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

this._calendarWidth = calendarRect.width.toString();
this._calendarHeight = calendarRect.height.toString();

const monthPicker = this.shadowRoot.querySelector("[ui5-monthpicker]");
monthPicker._selectedDates = [...this.selectedDates];
const currentMonthIndex = monthPicker._itemNav._getItems().findIndex(item => {
const calDate = CalendarDate.fromTimestamp(parseInt(item.timestamp) * 1000, this._primaryCalendarType);
return calDate.getMonth() === this._calendarDate.getMonth();
});
monthPicker._itemNav.currentIndex = currentMonthIndex;
this._header._isMonthButtonHidden = true;
}

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

this._calendarWidth = calendarRect.width.toString();
this._calendarHeight = calendarRect.height.toString();

const yearPicker = this.shadowRoot.querySelector("[ui5-yearpicker]");
yearPicker._selectedDates = [...this.selectedDates];
const currentYearIndex = yearPicker._itemNav._getItems().findIndex(item => {
const calDate = CalendarDate.fromTimestamp(parseInt(item.timestamp) * 1000, this._primaryCalendarType);
return calDate.getYear() === this._calendarDate.getYear();
});
yearPicker._itemNav.currentIndex = currentYearIndex;
}

_hideMonthPicker() {
Expand Down
14 changes: 13 additions & 1 deletion packages/main/src/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { isPhone, isIE } from "@ui5/webcomponents-base/dist/Device.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import "@ui5/webcomponents-icons/dist/appointment-2.js";
import "@ui5/webcomponents-icons/dist/decline.js";
import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js";
import { DATEPICKER_OPEN_ICON_TITLE, DATEPICKER_DATE_ACC_TEXT, INPUT_SUGGESTIONS_TITLE } from "./generated/i18n/i18n-defaults.js";
import Icon from "./Icon.js";
import Button from "./Button.js";
Expand Down Expand Up @@ -419,14 +420,16 @@ class DatePicker extends UI5Element {
calendar._hideYearPicker();
}
},
afterOpen: () => {
afterOpen: async () => {
await RenderScheduler.whenFinished();
const calendar = this.calendar;

if (!calendar) {
return;
}

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

if (this.responsivePopover) {
const calendar = this.calendar;
const dayPicker = calendar.shadowRoot.querySelector(`#${calendar._id}-daypicker`);
// If day picker component rerendering is triggered due to a change in the date picker component input filed,
// then mark this trigger and avoid moving the focus from the date picker input field throughout the on after
// rendering hook of the day picker component
dayPicker._inputLiveChangeTrigger = true;
}

this.value = nextValue;
this.fireEvent("input", { value: nextValue, valid: isValid });
}
Expand Down
5 changes: 5 additions & 0 deletions packages/main/src/DayPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,11 @@ class DayPicker extends UI5Element {

onAfterRendering() {
this._fireDayPickerRendered();
if (this._inputLiveChangeTrigger) {
this._inputLiveChangeTrigger = false;
} else {
this._itemNav.focusCurrent();
}
}

_onmousedown(event) {
Expand Down
17 changes: 13 additions & 4 deletions packages/main/src/MonthPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const metadata = {
type: String,
},

_selectedDates: {
type: Integer,
multiple: true,
},

_quarters: {
type: Object,
multiple: true,
Expand Down Expand Up @@ -160,27 +165,31 @@ class MonthPicker extends UI5Element {
ItemNavigation.BORDER_REACH,
this._handleItemNavigationBorderReach.bind(this)
);

this._selectedDates = [];
}

onBeforeRendering() {
const localeData = getCachedLocaleDataInstance(getLocale());

const quarters = [];
const oCalDate = CalendarDate.fromTimestamp(new Date().getTime(), this._primaryCalendarType);
const oCalDate = this._calendarDate;
let timestamp;

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

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

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

Expand Down Expand Up @@ -233,8 +242,8 @@ class MonthPicker extends UI5Element {
_onmousedown(event) {
if (event.target.className.indexOf("ui5-mp-item") > -1) {
const targetTimestamp = this.getTimestampFromDOM(event.target);
const focusedItem = this._itemNav._getItems().find(item => parseInt(item.timestamp) === targetTimestamp);
this._itemNav.currentIndex = this._itemNav._getItems().indexOf(focusedItem);
const focusedItemIndex = this._itemNav._getItems().findIndex(item => parseInt(item.timestamp) === targetTimestamp);
this._itemNav.currentIndex = focusedItemIndex;
this._itemNav.focusCurrent();
}
}
Expand Down
19 changes: 15 additions & 4 deletions packages/main/src/YearPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ const metadata = {
defaultValue: undefined,
},

_selectedDates: {
type: Integer,
multiple: true,
},

_selectedYear: {
type: Integer,
noAttribute: true,
Expand Down Expand Up @@ -165,6 +170,7 @@ class YearPicker extends UI5Element {
);

this._yearIntervals = [];
this._selectedDates = [];
}

onBeforeRendering() {
Expand All @@ -190,6 +196,7 @@ class YearPicker extends UI5Element {
this._selectedYear = this._year;
}

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

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

Expand Down Expand Up @@ -256,8 +267,8 @@ class YearPicker extends UI5Element {
_onmousedown(event) {
if (event.target.className.indexOf("ui5-yp-item") > -1) {
const targetTimestamp = this.getTimestampFromDom(event.target);
const focusedItem = this._itemNav._getItems().find(item => parseInt(item.timestamp) === targetTimestamp);
this._itemNav.currentIndex = this._itemNav._getItems().indexOf(focusedItem);
const focusedItemIndex = this._itemNav._getItems().findIndex(item => parseInt(item.timestamp) === targetTimestamp);
this._itemNav.currentIndex = focusedItemIndex;
this._itemNav.focusCurrent();
}
}
Expand Down Expand Up @@ -413,7 +424,7 @@ class YearPicker extends UI5Element {
}

YearPicker._ITEMS_COUNT = 20;
YearPicker._MIDDLE_ITEM_INDEX = 7;
YearPicker._MIDDLE_ITEM_INDEX = 10;

YearPicker.define();

Expand Down
Loading

0 comments on commit 30d9d2b

Please sign in to comment.