From 4ae812f88aa24a3f0412a3a687870551b7aeee7a Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Fri, 30 Oct 2020 09:35:58 +0200 Subject: [PATCH 01/24] feat(ui5-calendar): component is no public and can be used standalone. Fixes #1730 --- packages/base/src/types/CalendarSelection.js | 5 + packages/main/src/Calendar.js | 112 ++++++++++++------- 2 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 packages/base/src/types/CalendarSelection.js diff --git a/packages/base/src/types/CalendarSelection.js b/packages/base/src/types/CalendarSelection.js new file mode 100644 index 000000000000..beab0aad2d09 --- /dev/null +++ b/packages/base/src/types/CalendarSelection.js @@ -0,0 +1,5 @@ +const CalendarSelection = { + Single: "Single", + Range: "Range" +}; +export default CalendarSelection; diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index e42b4a9c2c52..61390a795d61 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -7,6 +7,7 @@ import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js"; import LocaleData from "@ui5/webcomponents-localization/dist/LocaleData.js"; import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js"; import CalendarType from "@ui5/webcomponents-base/dist/types/CalendarType.js"; +import CalendarSelection from "@ui5/webcomponents-base/dist/types/CalendarSelection.js"; import Integer from "@ui5/webcomponents-base/dist/types/Integer.js"; import { isF4, isF4Shift } from "@ui5/webcomponents-base/dist/Keys.js"; import CalendarHeader from "./CalendarHeader.js"; @@ -32,7 +33,7 @@ const metadata = { /** * Defines the UNIX timestamp - seconds since 00:00:00 UTC on Jan 1, 1970. * @type {Integer} - * @public + * @private */ timestamp: { type: Integer, @@ -49,6 +50,21 @@ const metadata = { type: CalendarType, }, + /** + * Defines the type of selection used in the calendar component. + * The property takes as value an object of type CalendarSelection. + * Default value CalendarSelection.Single enables single selection + * mode in the calendar component. + * Other possible value is CalendarSelection.Range enables selection + * of a range in the calendar component. + * @type {CalendarSelection} + * @defaultvalue "Single" + * @public + */ + selection: { + type: CalendarSelection + }, + /** * Defines the selected dates as UTC timestamps. * @type {Array} @@ -64,7 +80,6 @@ const metadata = { * * @type {string} * @defaultvalue "" - * @since 1.0.0-rc.6 * @public */ minDate: { @@ -76,7 +91,6 @@ const metadata = { * * @type {string} * @defaultvalue "" - * @since 1.0.0-rc.6 * @public */ maxDate: { @@ -93,7 +107,6 @@ const metadata = { * @type {boolean} * @defaultvalue false * @public - * @since 1.0.0-rc.8 */ hideWeekNumbers: { type: Boolean, @@ -143,45 +156,61 @@ const metadata = { /** * @class * + *

Overview

+ * + * The ui5-calendar can be used standale to display the years, months, weeks and days + * but the main purpose of the ui5-calendar is to be used within a ui5-date-picker + *

+ * + *

Usage

+ * + * The user can navigate to a particular date by: + *
+ * + *
+ * The user can comfirm a date selection by pressing over a date inside the ui5-daypicker component. + *

+ * *

Keyboard Handling

-* The ui5-calendar provides advanced keyboard handling. -* If the ui5-calendar is focused the user can -* choose a picker by using the following shortcuts:
-* * @type {CalendarSelection} + * @defaultvalue "Single" * @public */ selection: { type: CalendarSelection, + defaultValue: CalendarSelection.Single, }, /** @@ -230,8 +232,6 @@ class DayPicker extends UI5Element { constructor() { super(); - this.selection = CalendarSelection.Single; - this._itemNav = new ItemNavigation(this, { rowSize: 7, pageSize: 42, diff --git a/packages/main/src/Select.js b/packages/main/src/Select.js index 142eaafb37a6..ea7c22f3bcb9 100644 --- a/packages/main/src/Select.js +++ b/packages/main/src/Select.js @@ -94,7 +94,7 @@ const metadata = { /** * Defines whether ui5-select is in disabled state. - *

S + *

* Note: A disabled ui5-select is noninteractive. * * @type {boolean} From 271c33b58e6790c1e51cf3609dd9fb1c69bc0459 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Mon, 16 Nov 2020 18:36:46 +0200 Subject: [PATCH 12/24] An issue with DateRangePicker is now fixed. A new range can be selected if there is already a selected range. The selected range from the input field is now applied to the Calendar component only when the popover is opened. --- packages/main/src/DatePicker.js | 7 ++--- packages/main/src/DateRangePicker.js | 45 +++++++--------------------- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/packages/main/src/DatePicker.js b/packages/main/src/DatePicker.js index 59ae271f616d..ffbbd65f687d 100644 --- a/packages/main/src/DatePicker.js +++ b/packages/main/src/DatePicker.js @@ -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 CalendarSelection from "@ui5/webcomponents-base/dist/types/CalendarSelection.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"; @@ -452,6 +453,7 @@ class DatePicker extends UI5Element { this._calendar = { onSelectedDatesChange: this._handleCalendarChange.bind(this), + selection: CalendarSelection.Single, selectedDates: [], }; @@ -479,11 +481,6 @@ class DatePicker extends UI5Element { this.maxDate = null; console.warn(`In order for the "maxDate" property to have effect, you should enter valid date format`); // eslint-disable-line } - if (this._checkValueValidity(this.value)) { - this._changeCalendarSelection(); - } else if (this.value !== "") { - this._calendar.selectedDates = []; - } const FormSupport = getFeature("FormSupport"); if (FormSupport) { diff --git a/packages/main/src/DateRangePicker.js b/packages/main/src/DateRangePicker.js index 1f541033c7dd..6a3f433d4ff0 100644 --- a/packages/main/src/DateRangePicker.js +++ b/packages/main/src/DateRangePicker.js @@ -112,10 +112,6 @@ class DateRangePicker extends DatePicker { constructor() { super(); - } - - onBeforeRendering(...params) { - super.onBeforeRendering(...params); this._calendar.selection = CalendarSelection.Range; } @@ -176,15 +172,12 @@ class DateRangePicker extends DatePicker { return; } - const oCalDate = this._calendarDate, - timestamp = focusTimestamp || oCalDate.valueOf() / 1000, - dates = this._splitValueByDelimiter(this.value); + const oCalDate = this._calendarDate; + const timestamp = focusTimestamp || oCalDate.valueOf() / 1000; this._calendar = Object.assign({}, this._calendar); this._calendar.timestamp = timestamp; - this._calendar.selectedDates = this.value && this._checkValueValidity(this.value) - ? [this._getTimeStampFromString(dates[0]) / 1000, this._getTimeStampFromString(dates[1]) / 1000] - : []; + this._calendar.selectedDates = this.value ? [this._firstDateTimestamp, this._lastDateTimestamp] : []; } get _calendarDate() { @@ -284,7 +277,7 @@ class DateRangePicker extends DatePicker { this.closePicker(); this._firstDateTimestamp = selectedDates[0] < selectedDates[1] ? selectedDates[0] : selectedDates[1]; this._lastDateTimestamp = selectedDates[0] > selectedDates[1] ? selectedDates[0] : selectedDates[1]; - const fireChange = this._handleCalendarSelectedDatesChange(event); + const fireChange = this._handleCalendarSelectedDatesChange(event, this._firstDateTimestamp); if (fireChange) { this.fireEvent("change", { value: this.value, valid: true }); @@ -294,7 +287,9 @@ class DateRangePicker extends DatePicker { } else { this._firstDateTimestamp = selectedDates[0]; this._lastDateTimestamp = undefined; - this._handleCalendarSelectedDatesChange(event); + this._calendar.timestamp = selectedDates[0]; + this._calendar.selectedDates = [...event.detail.dates]; + return false; } } @@ -417,30 +412,12 @@ class DateRangePicker extends DatePicker { } } - _handleCalendarSelectedDatesChange(event) { - this._updateValueCalendarSelectedDatesChange(); - - this._calendar.timestamp = this._firstDateTimestamp; - this._calendar.selectedDates = [...event.detail.dates]; - this._focusInputAfterClose = true; - - if (this.isInValidRange(this.value)) { - this.valueState = ValueState.None; - } else { - this.valueState = ValueState.Error; - } - - return true; - } - _updateValueCalendarSelectedDatesChange() { - if (this._firstDateTimestamp && this._lastDateTimestamp) { - const calStartDate = CalendarDate.fromTimestamp(this._firstDateTimestamp * 1000, this._primaryCalendarType); - const calEndDate = CalendarDate.fromTimestamp(this._lastDateTimestamp * 1000, this._primaryCalendarType); + const calStartDate = CalendarDate.fromTimestamp(this._firstDateTimestamp * 1000, this._primaryCalendarType); + const calEndDate = CalendarDate.fromTimestamp(this._lastDateTimestamp * 1000, this._primaryCalendarType); - this.value = this._formatValue(calStartDate.toLocalJSDate().valueOf() / 1000, calEndDate.toLocalJSDate().valueOf() / 1000); - this._prevValue = this.value; - } + this.value = this._formatValue(calStartDate.toLocalJSDate().valueOf() / 1000, calEndDate.toLocalJSDate().valueOf() / 1000); + this._prevValue = this.value; } /** From d145da716d33a9718f0df3e1d7b8bfe1f311e742 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Mon, 16 Nov 2020 19:15:45 +0200 Subject: [PATCH 13/24] The DateRangePicker selected range is applied again in the on before rendering hook. When a new range is being selected by user interaction the input field is getting cleared. --- packages/main/src/DatePicker.js | 6 ++++++ packages/main/src/DateRangePicker.js | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/main/src/DatePicker.js b/packages/main/src/DatePicker.js index ffbbd65f687d..ebd4779fba4f 100644 --- a/packages/main/src/DatePicker.js +++ b/packages/main/src/DatePicker.js @@ -482,6 +482,12 @@ class DatePicker extends UI5Element { console.warn(`In order for the "maxDate" property to have effect, you should enter valid date format`); // eslint-disable-line } + if (this._checkValueValidity(this.value)) { + this._changeCalendarSelection(); + } else if (this.value !== "") { + this._calendar.selectedDates = []; + } + const FormSupport = getFeature("FormSupport"); if (FormSupport) { FormSupport.syncNativeHiddenInput(this); diff --git a/packages/main/src/DateRangePicker.js b/packages/main/src/DateRangePicker.js index 6a3f433d4ff0..82a256e3d033 100644 --- a/packages/main/src/DateRangePicker.js +++ b/packages/main/src/DateRangePicker.js @@ -174,10 +174,11 @@ class DateRangePicker extends DatePicker { const oCalDate = this._calendarDate; const timestamp = focusTimestamp || oCalDate.valueOf() / 1000; + const dates = this._splitValueByDelimiter(this.value); this._calendar = Object.assign({}, this._calendar); this._calendar.timestamp = timestamp; - this._calendar.selectedDates = this.value ? [this._firstDateTimestamp, this._lastDateTimestamp] : []; + this._calendar.selectedDates = this.value ? [this._getTimeStampFromString(dates[0]) / 1000, this._getTimeStampFromString(dates[1]) / 1000] : []; } get _calendarDate() { @@ -289,6 +290,7 @@ class DateRangePicker extends DatePicker { this._lastDateTimestamp = undefined; this._calendar.timestamp = selectedDates[0]; this._calendar.selectedDates = [...event.detail.dates]; + this.value = ""; return false; } } From bf9c7ff12f6ec9b43cc394fe68a627c1276297d0 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Thu, 19 Nov 2020 17:35:21 +0200 Subject: [PATCH 14/24] Documenation recommendations are applied. --- packages/main/src/Calendar.js | 2 +- packages/main/test/samples/Calendar.sample.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index cac0ae930280..e7dee705d8b4 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -166,7 +166,7 @@ const metadata = { * *

Overview

* - * The ui5-calendar can be used standale to display the years, months, weeks and days + * The ui5-calendar can be used stand alone to display the years, months, weeks and days * but the main purpose of the ui5-calendar is to be used within a ui5-date-picker *

* diff --git a/packages/main/test/samples/Calendar.sample.html b/packages/main/test/samples/Calendar.sample.html index 811ab901277a..444a31ef126c 100644 --- a/packages/main/test/samples/Calendar.sample.html +++ b/packages/main/test/samples/Calendar.sample.html @@ -43,7 +43,7 @@

Calendar with hidden week numbers

-

Calendar with multiple selection mode

+

Calendar with selection type Multiple

@@ -55,7 +55,7 @@

Calendar with multiple selection mode

-

Calendar with range selection mode

+

Calendar with selection type Range

From 86a5e8158995045b137df8893f176eb048f79a64 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Fri, 20 Nov 2020 15:41:00 +0200 Subject: [PATCH 15/24] - Getters are created for the picker DOM elements. - Functions are created in order to handle tab and shift tab keyboard keys press. --- packages/base/src/types/CalendarSelection.js | 2 +- packages/main/src/Calendar.js | 151 +++++++++++-------- 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/packages/base/src/types/CalendarSelection.js b/packages/base/src/types/CalendarSelection.js index 99043c14bb9b..3850573e37b9 100644 --- a/packages/base/src/types/CalendarSelection.js +++ b/packages/base/src/types/CalendarSelection.js @@ -12,6 +12,6 @@ class CalendarSelection extends DataType { } } -CalendarSelection.generataTypeAcessors(CalendarSelections); +CalendarSelection.generateTypeAcessors(CalendarSelections); export default CalendarSelection; diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index e7dee705d8b4..cb49756cac84 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -175,11 +175,11 @@ const metadata = { * The user can navigate to a particular date by: *
*
    - *
  • Pressing over a month inside the ui5-monthpicker component
  • - *
  • Pressing over an year inside the ui5-yearpicker component
  • + *
  • Pressing over a month inside the months view
  • + *
  • Pressing over an year inside the years view
  • *
*
- * The user can comfirm a date selection by pressing over a date inside the ui5-daypicker component. + * The user can comfirm a date selection by pressing over a date inside the days view. *

* *

Keyboard Handling

@@ -226,7 +226,7 @@ const metadata = { * @extends sap.ui.webcomponents.base.UI5Element * @tagname ui5-calendar * @public - * @since 1.0.0-rc.10 + * @since 1.0.0-rc.11 */ class Calendar extends UI5Element { static get metadata() { @@ -311,15 +311,13 @@ class Calendar extends UI5Element { } onAfterRendering() { - const header = this.shadowRoot.querySelector("ui5-calendar-header"); - header.shadowRoot.querySelector("[data-sap-show-picker='Month']").setAttribute("tabindex", "-1"); - header.shadowRoot.querySelector("[data-sap-show-picker='Year']").setAttribute("tabindex", "-1"); + this.monthButton.setAttribute("tabindex", "-1"); + this.yearButton.setAttribute("tabindex", "-1"); - const dayPicker = this.shadowRoot.querySelector("ui5-daypicker"); const currentTimestamp = new CalendarDate(this._calendarDate, this._primaryCalendarType).valueOf() / 1000; - const newItemIndex = dayPicker._itemNav._getItems().findIndex(day => parseInt(day.timestamp) === currentTimestamp); - dayPicker._itemNav.currentIndex = newItemIndex; - dayPicker._itemNav.update(); + const newItemIndex = this.dayPicker._itemNav._getItems().findIndex(day => parseInt(day.timestamp) === currentTimestamp); + this.dayPicker._itemNav.currentIndex = newItemIndex; + this.dayPicker._itemNav.update(); } _refreshNavigationButtonsState() { @@ -386,6 +384,30 @@ class Calendar extends UI5Element { } } + get dayPicker() { + return this.shadowRoot.querySelector("ui5-daypicker"); + } + + get monthPicker() { + return this.shadowRoot.querySelector("ui5-monthpicker"); + } + + get yearPicker() { + return this.shadowRoot.querySelector("ui5-yearpicker"); + } + + get header() { + return this.shadowRoot.querySelector("ui5-calendar-header"); + } + + get monthButton() { + return this.header.shadowRoot.querySelector("[data-sap-show-picker='Month']"); + } + + get yearButton() { + return this.header.shadowRoot.querySelector("[data-sap-show-picker='Year']"); + } + get _timestamp() { return this.timestamp !== undefined ? this.timestamp : Math.floor(new Date().getTime() / 1000); } @@ -470,75 +492,75 @@ class Calendar extends UI5Element { } if (isTabNext(event)) { - const header = this.shadowRoot.querySelector("ui5-calendar-header"); - const monthButton = header.shadowRoot.querySelector("[data-sap-show-picker='Month']"); - const yearButton = header.shadowRoot.querySelector("[data-sap-show-picker='Year']"); - const target = event.target; - - if (target.tagName === "UI5-DAYPICKER" || target.tagName === "UI5-MONTHPICKER" || target.tagName === "UI5-YEARPICKER") { - if (monthButton.getAttribute("hidden") === null) { - monthButton.focus(); - } else { - yearButton.focus(); - } - event.preventDefault(); - } else if (event.target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") { - yearButton.focus(); - event.preventDefault(); + this._handleTabNext(event); + } + + if (isTabPrevious(event)) { + this._handleTabPrevous(event); + } + } + + _handleTabNext(event) { + const target = event.target; + + if (target.tagName === "UI5-DAYPICKER" || target.tagName === "UI5-MONTHPICKER" || target.tagName === "UI5-YEARPICKER") { + if (this.monthButton.getAttribute("hidden") === null) { + this.monthButton.focus(); } else { - this._setPickerCurrentTabindex(-1); + this.yearButton.focus(); } + event.preventDefault(); + } else if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") { + this.yearButton.focus(); + event.preventDefault(); + } else { + this._setPickerCurrentTabindex(-1); } + } - if (isTabPrevious(event)) { - const header = this.shadowRoot.querySelector("ui5-calendar-header"); - if (event.target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") { + _handleTabPrevous(event) { + const target = event.target; + + if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") { + this._moveFocusToPickerContent(); + event.preventDefault(); + } else if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Year") { + if (this.monthButton.getAttribute("hidden") === null) { + this.monthButton.focus(); + } else { this._moveFocusToPickerContent(); - event.preventDefault(); - } else if (event.target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Year") { - const monthButton = header.shadowRoot.querySelector("[data-sap-show-picker='Month']"); - if (monthButton.getAttribute("hidden") === null) { - monthButton.focus(); - } else { - this._moveFocusToPickerContent(); - } - event.preventDefault(); } + event.preventDefault(); } } _moveFocusToPickerContent() { if (!this._oMonth._hidden) { - this.shadowRoot.querySelector("ui5-daypicker")._itemNav.focusCurrent(); + this.dayPicker._itemNav.focusCurrent(); } else if (!this._monthPicker._hidden) { - this.shadowRoot.querySelector("ui5-monthpicker")._itemNav.focusCurrent(); + this.monthPicker._itemNav.focusCurrent(); } else { - this.shadowRoot.querySelector("ui5-yearpicker")._itemNav.focusCurrent(); + this.yearPicker._itemNav.focusCurrent(); } } _onfocusout(event) { - const header = this.shadowRoot.querySelector("ui5-calendar-header"); - header.shadowRoot.querySelector("[data-sap-show-picker='Month']").setAttribute("tabindex", "-1"); - header.shadowRoot.querySelector("[data-sap-show-picker='Year']").setAttribute("tabindex", "-1"); + this.monthButton.setAttribute("tabindex", "-1"); + this.yearButton.setAttribute("tabindex", "-1"); this._setPickerCurrentTabindex(0); } _setPickerCurrentTabindex(index) { - const dayPicker = this.shadowRoot.querySelector("ui5-daypicker"); - const monthPicker = this.shadowRoot.querySelector("ui5-monthpicker"); - const yearPicker = this.shadowRoot.querySelector("ui5-yearpicker"); - - if (dayPicker) { - dayPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + if (this.dayPicker) { + this.dayPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); } - if (monthPicker) { - monthPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + if (this.monthPicker) { + this.monthPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); } - if (yearPicker) { - yearPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + if (this.yearPicker) { + this.yearPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); } } @@ -581,16 +603,14 @@ class Calendar extends UI5Element { let fistDayOfMonthIndex = -1; // focus first day of the month - const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]"); - - dayPicker._getVisibleDays(targetDate).forEach((date, index) => { + this.dayPicker._getVisibleDays(targetDate).forEach((date, index) => { if (date.getDate() === 1 && (fistDayOfMonthIndex === -1)) { fistDayOfMonthIndex = index; } }); - dayPicker._itemNav.currentIndex = fistDayOfMonthIndex; - dayPicker._itemNav.focusCurrent(); + this.dayPicker._itemNav.currentIndex = fistDayOfMonthIndex; + this.dayPicker._itemNav.focusCurrent(); } _handleSelectedYearChange(event) { @@ -665,9 +685,8 @@ class Calendar extends UI5Element { const minCalendarDateYear = CalendarDate.fromTimestamp(this._getMinCalendarDate(), this._primaryCalendarType).getYear(); // focus first day of the month - const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]"); - const currentMonthDate = dayPicker._calendarDate.setMonth(dayPicker._calendarDate.getMonth()); - const lastMonthDate = dayPicker._calendarDate.setMonth(dayPicker._calendarDate.getMonth() - 1); + const currentMonthDate = this.dayPicker._calendarDate.setMonth(this.dayPicker._calendarDate.getMonth()); + const lastMonthDate = this.dayPicker._calendarDate.setMonth(this.dayPicker._calendarDate.getMonth() - 1); // set the date to last day of last month currentMonthDate.setDate(-1); @@ -679,7 +698,7 @@ class Calendar extends UI5Element { return; } - dayPicker._getVisibleDays(lastMonthDate).forEach((date, index) => { + this.dayPicker._getVisibleDays(lastMonthDate).forEach((date, index) => { const isSameDate = currentMonthDate.getDate() === date.getDate(); const isSameMonth = currentMonthDate.getMonth() === date.getMonth(); @@ -692,10 +711,10 @@ class Calendar extends UI5Element { 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 = this.dayPicker.shadowRoot.querySelector(".ui5-dp-content").children[parseInt(lastDayOfMonthIndex / weekDaysCount) + 1].children[(lastDayOfMonthIndex % weekDaysCount)]; // update current item in ItemNavigation - dayPicker._itemNav.current = lastDayOfMonthIndex; + this.dayPicker._itemNav.current = lastDayOfMonthIndex; // focus the item lastDay.focus(); From 080185039ff0921f78494c459e23c6effc0bc309 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Wed, 25 Nov 2020 10:55:02 +0200 Subject: [PATCH 16/24] Empty new line is removed from the "CalendarSelection" file. --- packages/base/src/types/CalendarSelection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base/src/types/CalendarSelection.js b/packages/base/src/types/CalendarSelection.js index 3850573e37b9..1d756d9ba827 100644 --- a/packages/base/src/types/CalendarSelection.js +++ b/packages/base/src/types/CalendarSelection.js @@ -14,4 +14,4 @@ class CalendarSelection extends DataType { CalendarSelection.generateTypeAcessors(CalendarSelections); -export default CalendarSelection; +export default CalendarSelection; \ No newline at end of file From 4bba3a0e3c249ebbcc6d9883aa3a4b750251cba8 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Wed, 25 Nov 2020 12:03:45 +0200 Subject: [PATCH 17/24] Lint erros fixed. --- packages/main/src/Calendar.js | 2 +- packages/main/src/CalendarHeader.hbs | 4 ++-- packages/main/src/CalendarHeader.js | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index e188d4646704..e8a8528fb8b8 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -710,7 +710,7 @@ class Calendar extends UI5Element { if (lastDayOfMonthIndex !== -1) { // find the DOM for the last day index - const lastDay = dayPicker.shadowRoot.querySelectorAll(".ui5-dp-content .ui5-dp-item")[lastDayOfMonthIndex]; + const lastDay = this.dayPicker.shadowRoot.querySelectorAll(".ui5-dp-content .ui5-dp-item")[lastDayOfMonthIndex]; // update current item in ItemNavigation this.dayPicker._itemNav.current = lastDayOfMonthIndex; diff --git a/packages/main/src/CalendarHeader.hbs b/packages/main/src/CalendarHeader.hbs index cab203d3ceac..9dffe8e188d8 100644 --- a/packages/main/src/CalendarHeader.hbs +++ b/packages/main/src/CalendarHeader.hbs @@ -26,7 +26,7 @@ class="ui5-calheader-arrowbtn ui5-calheader-middlebtn" ?hidden="{{_isMonthButtonHidden}}" type="{{_btn1.type}}" - tabindex="0" + tabindex="{{_bt1.tabIndex}}" @click={{_showMonthPicker}} data-sap-show-picker="Month" > @@ -37,7 +37,7 @@ id="{{_id}}-btn2" class="ui5-calheader-arrowbtn ui5-calheader-middlebtn" type="{{_btn2.type}}" - tabindex="0" + tabindex="{{_bt2.tabIndex}}" @click={{_showYearPicker}} data-sap-show-picker="Year" > diff --git a/packages/main/src/CalendarHeader.js b/packages/main/src/CalendarHeader.js index 65470d6f6d1d..cf7e97ac1512 100644 --- a/packages/main/src/CalendarHeader.js +++ b/packages/main/src/CalendarHeader.js @@ -86,9 +86,11 @@ class CalendarHeader extends UI5Element { this._btn1 = {}; this._btn1.type = ButtonDesign.Transparent; + this._btn1.tabIndex = "0"; this._btn2 = {}; this._btn2.type = ButtonDesign.Transparent; + this._btn2.tabIndex = "0"; this.i18nBundle = getI18nBundle("@ui5/webcomponents"); } From b7d4848467f08fcd50bc5215636f853ac0a1530d Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Wed, 25 Nov 2020 13:16:53 +0200 Subject: [PATCH 18/24] Lint erros fixed. --- packages/base/src/types/CalendarSelection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base/src/types/CalendarSelection.js b/packages/base/src/types/CalendarSelection.js index 1d756d9ba827..3850573e37b9 100644 --- a/packages/base/src/types/CalendarSelection.js +++ b/packages/base/src/types/CalendarSelection.js @@ -14,4 +14,4 @@ class CalendarSelection extends DataType { CalendarSelection.generateTypeAcessors(CalendarSelections); -export default CalendarSelection; \ No newline at end of file +export default CalendarSelection; From 591d94fab8a5699c2b80e7fa9838cc2d3f81b534 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Wed, 25 Nov 2020 16:26:34 +0200 Subject: [PATCH 19/24] - Separate methods are created for the picker componets, in order to change the current item navigation tab index. - The tab index of the calendar header buttons is changed through the calendar header template. --- packages/main/src/Calendar.hbs | 1 + packages/main/src/Calendar.js | 13 +++++-------- packages/main/src/CalendarHeader.hbs | 4 ++-- packages/main/src/CalendarHeader.js | 6 ++++-- packages/main/src/DayPicker.js | 4 ++++ packages/main/src/MonthPicker.js | 4 ++++ packages/main/src/YearPicker.js | 4 ++++ 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/main/src/Calendar.hbs b/packages/main/src/Calendar.hbs index e589d8f6d385..50ed216837a2 100644 --- a/packages/main/src/Calendar.hbs +++ b/packages/main/src/Calendar.hbs @@ -17,6 +17,7 @@ ._isNextButtonDisabled="{{_header._isNextButtonDisabled}}" ._isPrevButtonDisabled="{{_header._isPrevButtonDisabled}}" ._isMonthButtonHidden="{{_header._isMonthButtonHidden}}" + ._tabIndex="{{_header.tabIndex}}" >
diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index e8a8528fb8b8..55ec8a64f888 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -293,6 +293,7 @@ class Calendar extends UI5Element { this._oMonth.maxDate = this.maxDate; this._header.monthText = localeData.getMonths("wide", this._primaryCalendarType)[this._month]; this._header.yearText = oYearFormat.format(this._localDate, true); + this._header.tabIndex = "-1"; // month picker this._monthPicker.primaryCalendarType = this._primaryCalendarType; @@ -311,9 +312,6 @@ class Calendar extends UI5Element { } onAfterRendering() { - this.monthButton.setAttribute("tabindex", "-1"); - this.yearButton.setAttribute("tabindex", "-1"); - const currentTimestamp = new CalendarDate(this._calendarDate, this._primaryCalendarType).valueOf() / 1000; const newItemIndex = this.dayPicker._itemNav._getItems().findIndex(day => parseInt(day.timestamp) === currentTimestamp); this.dayPicker._itemNav.currentIndex = newItemIndex; @@ -545,22 +543,21 @@ class Calendar extends UI5Element { } _onfocusout(event) { - this.monthButton.setAttribute("tabindex", "-1"); - this.yearButton.setAttribute("tabindex", "-1"); + this._header.tabIndex = "-1"; this._setPickerCurrentTabindex(0); } _setPickerCurrentTabindex(index) { if (this.dayPicker) { - this.dayPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + this.dayPicker._setCurrentItemTabIndex(index); } if (this.monthPicker) { - this.monthPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + this.monthPicker._setCurrentItemTabIndex(index); } if (this.yearPicker) { - this.yearPicker._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + this.yearPicker._setCurrentItemTabIndex(index); } } diff --git a/packages/main/src/CalendarHeader.hbs b/packages/main/src/CalendarHeader.hbs index 9dffe8e188d8..aa23e27e30b0 100644 --- a/packages/main/src/CalendarHeader.hbs +++ b/packages/main/src/CalendarHeader.hbs @@ -26,7 +26,7 @@ class="ui5-calheader-arrowbtn ui5-calheader-middlebtn" ?hidden="{{_isMonthButtonHidden}}" type="{{_btn1.type}}" - tabindex="{{_bt1.tabIndex}}" + tabindex="{{_tabIndex}}" @click={{_showMonthPicker}} data-sap-show-picker="Month" > @@ -37,7 +37,7 @@ id="{{_id}}-btn2" class="ui5-calheader-arrowbtn ui5-calheader-middlebtn" type="{{_btn2.type}}" - tabindex="{{_bt2.tabIndex}}" + tabindex="{{_tabIndex}}" @click={{_showYearPicker}} data-sap-show-picker="Year" > diff --git a/packages/main/src/CalendarHeader.js b/packages/main/src/CalendarHeader.js index cf7e97ac1512..4244902f1e21 100644 --- a/packages/main/src/CalendarHeader.js +++ b/packages/main/src/CalendarHeader.js @@ -46,6 +46,10 @@ const metadata = { _isMonthButtonHidden: { type: Boolean, }, + _tabIndex: { + type: String, + defaultValue: "0", + }, }, events: { "previous-press": {}, @@ -86,11 +90,9 @@ class CalendarHeader extends UI5Element { this._btn1 = {}; this._btn1.type = ButtonDesign.Transparent; - this._btn1.tabIndex = "0"; this._btn2 = {}; this._btn2.type = ButtonDesign.Transparent; - this._btn2.tabIndex = "0"; this.i18nBundle = getI18nBundle("@ui5/webcomponents"); } diff --git a/packages/main/src/DayPicker.js b/packages/main/src/DayPicker.js index c66d6a4342e5..f0b56e211930 100644 --- a/packages/main/src/DayPicker.js +++ b/packages/main/src/DayPicker.js @@ -681,6 +681,10 @@ class DayPicker extends UI5Element { return this.i18nBundle.getText(DAY_PICKER_NON_WORKING_DAY); } + _setCurrentItemTabIndex(index) { + this._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + } + _modifySelectionAndNotifySubscribers(timestamp) { switch (this.selection) { case CalendarSelection.Single: diff --git a/packages/main/src/MonthPicker.js b/packages/main/src/MonthPicker.js index d2bae6defc9e..29180c7653ea 100644 --- a/packages/main/src/MonthPicker.js +++ b/packages/main/src/MonthPicker.js @@ -239,6 +239,10 @@ class MonthPicker extends UI5Element { return this._formatPattern !== "medium" && this._formatPattern !== "short" && this._formatPattern !== "long"; } + _setCurrentItemTabIndex(index) { + this._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + } + _onmousedown(event) { if (event.target.className.indexOf("ui5-mp-item") > -1) { const targetTimestamp = this.getTimestampFromDOM(event.target); diff --git a/packages/main/src/YearPicker.js b/packages/main/src/YearPicker.js index d08d59308ab0..a01efa790cdb 100644 --- a/packages/main/src/YearPicker.js +++ b/packages/main/src/YearPicker.js @@ -264,6 +264,10 @@ class YearPicker extends UI5Element { return this._formatPattern !== "medium" && this._formatPattern !== "short" && this._formatPattern !== "long"; } + _setCurrentItemTabIndex(index) { + this._itemNav._getCurrentItem().setAttribute("tabindex", index.toString()); + } + _onmousedown(event) { if (event.target.className.indexOf("ui5-yp-item") > -1) { const targetTimestamp = this.getTimestampFromDom(event.target); From 9891406654302647be398865a2c8096f3fa1841a Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Thu, 26 Nov 2020 01:45:58 +0200 Subject: [PATCH 20/24] feat(ui5-calendar): stand alone usage - ui5-calendar tests adjusted - When "Range" or "Multiple" type of selection is configured, then the last selected date by user interaction is also the navigated date. - ui5-daterange-picker now works properly with only the start date of the range typed into the input field. --- packages/main/src/Calendar.js | 30 +++++++++++-------- packages/main/src/DatePicker.js | 16 ++-------- packages/main/src/DateRangePicker.js | 36 +++++++---------------- packages/main/src/DayPicker.js | 6 ---- packages/main/test/specs/Calendar.spec.js | 8 ++--- 5 files changed, 34 insertions(+), 62 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index 55ec8a64f888..a9898e6193da 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -28,6 +28,7 @@ import CalendarTemplate from "./generated/templates/CalendarTemplate.lit.js"; // Styles import calendarCSS from "./generated/themes/Calendar.css.js"; +import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js"; /** * @public @@ -312,10 +313,7 @@ class Calendar extends UI5Element { } onAfterRendering() { - const currentTimestamp = new CalendarDate(this._calendarDate, this._primaryCalendarType).valueOf() / 1000; - const newItemIndex = this.dayPicker._itemNav._getItems().findIndex(day => parseInt(day.timestamp) === currentTimestamp); - this.dayPicker._itemNav.currentIndex = newItemIndex; - this.dayPicker._itemNav.update(); + this._setDayPickerCurrentIndex(this._calendarDate, false); } _refreshNavigationButtonsState() { @@ -562,9 +560,10 @@ class Calendar extends UI5Element { } _handleSelectedDatesChange(event) { - this.timestamp = event.detail.dates[0]; - this.selectedDates = [...event.detail.dates]; - this.fireEvent("selected-dates-change", { dates: event.detail.dates }); + const selectedDates = event.detail.dates; + this.timestamp = selectedDates[selectedDates.length - 1]; + this.selectedDates = [...selectedDates]; + this.fireEvent("selected-dates-change", { dates: selectedDates }); } _handleMonthNavigate(event) { @@ -604,7 +603,7 @@ class Calendar extends UI5Element { this._monthPicker.timestamp = this.timestamp; this._hideMonthPicker(); - this._setDayPickerCurrentIndex(oNewDate); + this._setDayPickerCurrentIndex(oNewDate, true); } _handleSelectedYearChange(event) { @@ -616,14 +615,19 @@ class Calendar extends UI5Element { this._yearPicker.timestamp = this.timestamp; this._hideYearPicker(); - this._setDayPickerCurrentIndex(oNewDate); + this._setDayPickerCurrentIndex(oNewDate, true); } - _setDayPickerCurrentIndex(calDate) { + async _setDayPickerCurrentIndex(calDate, applyFocus) { + await RenderScheduler.whenFinished(); 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; + const currentDateIndex = this.dayPicker._getVisibleDays(currentDate).findIndex(date => date.valueOf() === currentDate.valueOf()); + this.dayPicker._itemNav.currentIndex = currentDateIndex; + if (applyFocus) { + this.dayPicker._itemNav.focusCurrent(); + } else { + this.dayPicker._itemNav.update(); + } } _handleMonthButtonPress() { diff --git a/packages/main/src/DatePicker.js b/packages/main/src/DatePicker.js index 01c0b5b8a2d8..8e4b07da60dd 100644 --- a/packages/main/src/DatePicker.js +++ b/packages/main/src/DatePicker.js @@ -430,7 +430,6 @@ class DatePicker extends UI5Element { } 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; @@ -672,15 +671,6 @@ 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 }); } @@ -984,15 +974,13 @@ class DatePicker extends UI5Element { } } - _changeCalendarSelection(focusTimestamp) { + _changeCalendarSelection() { if (this._calendarDate.getYear() < 1) { // 0 is a valid year, but we cannot display it return; } - const oCalDate = this._calendarDate; - const timestamp = focusTimestamp || oCalDate.valueOf() / 1000; - + const timestamp = this._calendarDate.valueOf() / 1000; this._calendar = Object.assign({}, this._calendar); this._calendar.timestamp = timestamp; this._calendar.selectedDates = this.value ? [timestamp] : []; diff --git a/packages/main/src/DateRangePicker.js b/packages/main/src/DateRangePicker.js index 85445269b3ca..d708332b52d1 100644 --- a/packages/main/src/DateRangePicker.js +++ b/packages/main/src/DateRangePicker.js @@ -169,24 +169,22 @@ class DateRangePicker extends DatePicker { this._prevValue = this.value; } - _changeCalendarSelection(focusTimestamp) { + _changeCalendarSelection() { if (this._calendarDate.getYear() < 1) { // 0 is a valid year, but we cannot display it return; } - const oCalDate = this._calendarDate; - const timestamp = focusTimestamp || oCalDate.valueOf() / 1000; + const timestamp = this._calendarDate.valueOf() / 1000; const dates = this._splitValueByDelimiter(this.value); - this._calendar = Object.assign({}, this._calendar); this._calendar.timestamp = timestamp; - this._calendar.selectedDates = this.value ? [this._getTimeStampFromString(dates[0]) / 1000, this._getTimeStampFromString(dates[1]) / 1000] : []; + this._calendar.selectedDates = dates.map(date => this._getTimeStampFromString(date) / 1000); } get _calendarDate() { - const dates = this._splitValueByDelimiter(this.value), - value = this._checkValueValidity(this.value) ? dates[0] : this.getFormat().format(new Date()), + const dateStrings = this._splitValueByDelimiter(this.value), + value = Boolean(this.value) && this._checkValueValidity(this.value) ? dateStrings[0] : this.getFormat().format(new Date()), millisecondsUTCFirstDate = value ? this.getFormat().parse(value, true).getTime() : this.getFormat().parse(this.validValue, true).getTime(), oCalDateFirst = CalendarDate.fromTimestamp( millisecondsUTCFirstDate - (millisecondsUTCFirstDate % (24 * 60 * 60 * 1000)), @@ -252,27 +250,15 @@ class DateRangePicker extends DatePicker { } isValid(value) { - const dateStrings = this._splitValueByDelimiter(value, this.delimiter), - isFirstDateValid = super.isValid(dateStrings[0]), - isLastDateValid = super.isValid(dateStrings[1]); - - if (!dateStrings[1]) { - return isFirstDateValid; - } - - return isFirstDateValid && isLastDateValid; + return this._splitValueByDelimiter(value) + .map(dateString => super.isValid(dateString)) + .every(valid => valid); } isInValidRange(value) { - const dateStrings = this._splitValueByDelimiter(value, this.delimiter), - isFirstDateInValidRange = super.isInValidRange(this._getTimeStampFromString(dateStrings[0])), - isLastDateInValidRange = super.isInValidRange(this._getTimeStampFromString(dateStrings[1])); - - if (!dateStrings[1]) { - return isFirstDateInValidRange; - } - - return isFirstDateInValidRange && isLastDateInValidRange; + return this._splitValueByDelimiter(value) + .map(dateString => super.isInValidRange(this._getTimeStampFromString(dateString))) + .every(valid => valid); } _handleCalendarChange(event) { diff --git a/packages/main/src/DayPicker.js b/packages/main/src/DayPicker.js index f0b56e211930..43169167ab6f 100644 --- a/packages/main/src/DayPicker.js +++ b/packages/main/src/DayPicker.js @@ -409,12 +409,6 @@ class DayPicker extends UI5Element { this._updateSelectionBetween(dayItems, firstTimestamp, lastTimestamp); } - - if (this._inputLiveChangeTrigger) { - this._inputLiveChangeTrigger = false; - } else { - this._itemNav.focusCurrent(); - } } _getVisualizedSelectedDates() { diff --git a/packages/main/test/specs/Calendar.spec.js b/packages/main/test/specs/Calendar.spec.js index 563ced5ae24e..dc792f53548d 100644 --- a/packages/main/test/specs/Calendar.spec.js +++ b/packages/main/test/specs/Calendar.spec.js @@ -90,18 +90,18 @@ describe("Calendar general interaction", () => { const calendar = browser.$("#calendar1"); const yearPicker = calendar.shadow$("ui5-yearpicker"); const YEAR = 1997; - calendar.setAttribute("timestamp", Date.UTC(YEAR) / 1000) + calendar.setAttribute("timestamp", Date.UTC(YEAR) / 1000); calendar.shadow$("ui5-calendar-header").shadow$(`div[data-sap-show-picker="Year"]`).click(); assert.strictEqual(yearPicker.getProperty("_selectedYear"), YEAR, "Year is set"); - calendar.shadow$("ui5-yearpicker").shadow$(`div[id="ui5wc_5-y852076800"]`).click(); + calendar.shadow$("ui5-yearpicker").shadow$(`div[data-sap-timestamp="852076800"]`).click(); }); it("Calendar doesn't mark year as selected when there are no selected dates", () => { const calendar = browser.$("#calendar1"); calendar.setAttribute("timestamp", new Date(Date.UTC(2000, 10, 1, 0, 0, 0)).valueOf() / 1000); calendar.shadow$("ui5-calendar-header").shadow$(`div[data-sap-show-picker="Year"]`).click(); - const focusedItem = calendar.shadow$("ui5-yearpicker").shadow$(`div[id="ui5wc_5-y946684800"]`); + const focusedItem = calendar.shadow$("ui5-yearpicker").shadow$(`[data-sap-timestamp="946684800"]`); assert.ok(focusedItem.isFocusedDeep(), "Current year element is the acrive element"); assert.notOk(focusedItem.hasClass("ui5-mp-item--selected"), "Current year is not selected"); @@ -112,7 +112,7 @@ describe("Calendar general interaction", () => { const calendar = browser.$("#calendar1"); calendar.setAttribute("timestamp", new Date(Date.UTC(2000, 10, 1, 0, 0, 0)).valueOf() / 1000); calendar.shadow$("ui5-calendar-header").shadow$(`div[data-sap-show-picker="Month"]`).click(); - const focusedItem = calendar.shadow$("ui5-monthpicker").shadow$(`div[id="ui5wc_4-m10"]`); + const focusedItem = calendar.shadow$("ui5-monthpicker").shadow$(`[data-sap-timestamp="973036800"]`); assert.ok(focusedItem.isFocusedDeep(), "Current month element is the acrive element"); assert.notOk(focusedItem.hasClass("ui5-mp-item--selected"), "Current month is not selected"); From 087000944a604ae767278e4e022ed719ef78ce70 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Thu, 26 Nov 2020 10:59:09 +0200 Subject: [PATCH 21/24] feat(ui5-calendar): component can be used standalone - Lint erros fixed. --- packages/main/src/Calendar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index a9898e6193da..868f5d1e9ca8 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -15,6 +15,7 @@ import { isTabNext, isTabPrevious, } from "@ui5/webcomponents-base/dist/Keys.js"; +import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js"; import CalendarHeader from "./CalendarHeader.js"; import DayPicker from "./DayPicker.js"; import MonthPicker from "./MonthPicker.js"; @@ -28,7 +29,6 @@ import CalendarTemplate from "./generated/templates/CalendarTemplate.lit.js"; // Styles import calendarCSS from "./generated/themes/Calendar.css.js"; -import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js"; /** * @public @@ -560,7 +560,7 @@ class Calendar extends UI5Element { } _handleSelectedDatesChange(event) { - const selectedDates = event.detail.dates; + const selectedDates = event.detail.dates; this.timestamp = selectedDates[selectedDates.length - 1]; this.selectedDates = [...selectedDates]; this.fireEvent("selected-dates-change", { dates: selectedDates }); From 82eaace11a9560d9ddca4b3739bd5db2da5e1e0c Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Thu, 26 Nov 2020 18:47:54 +0200 Subject: [PATCH 22/24] feat(ui5-calendar): component can be used stand alone - playground sample since tag is added - playground sample visualization is adjusted --- packages/main/src/Calendar.js | 1 - .../main/test/samples/Calendar.sample.html | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index 868f5d1e9ca8..4c18bf2f584e 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -168,7 +168,6 @@ const metadata = { *

Overview

* * The ui5-calendar can be used stand alone to display the years, months, weeks and days - * but the main purpose of the ui5-calendar is to be used within a ui5-date-picker *

* *

Usage

diff --git a/packages/main/test/samples/Calendar.sample.html b/packages/main/test/samples/Calendar.sample.html index 444a31ef126c..6c13ac41a24f 100644 --- a/packages/main/test/samples/Calendar.sample.html +++ b/packages/main/test/samples/Calendar.sample.html @@ -1,5 +1,8 @@ -
+

Calendar

+
+ +
@ui5/webcomponents
@@ -14,7 +17,7 @@

Basic Calendar


-		<ui5-calendar></ui5-calendar>
+<ui5-calendar></ui5-calendar>
 	
@@ -26,7 +29,7 @@

Calendar with Minimum and Maximum Date - 7/7/2020 - 20/10/2020 format-patter

-		<ui5-calendar min-date="7/7/2020" max-date="20/10/2020" format-pattern="dd/MM/yyyy"></ui5-calendar>
+<ui5-calendar min-date="7/7/2020" max-date="20/10/2020" format-pattern="dd/MM/yyyy"></ui5-calendar>
 	
@@ -38,7 +41,7 @@

Calendar with hidden week numbers


-		<ui5-calendar hide-week-numbers></ui5-calendar>
+<ui5-calendar hide-week-numbers></ui5-calendar>
 	
@@ -50,7 +53,7 @@

Calendar with selection type Multiple


-		<ui5-calendar selection="Multiple"></ui5-calendar>
+<ui5-calendar selection="Multiple"></ui5-calendar>
 	
@@ -62,43 +65,43 @@

Calendar with selection type Range


-		<ui5-calendar selection="Range"></ui5-calendar>
+<ui5-calendar selection="Range"></ui5-calendar>
 	
-

Calendar with Japanese Calendar Type

+

Japanese Calendar


-		<ui5-calendar primary-calendar-type='Japanese'></ui5-calendar>
+<ui5-calendar primary-calendar-type='Japanese'></ui5-calendar>
 	
-

Calendar with Islamic Calendar Type

+

Islamic Calendar


-		<ui5-calendar primary-calendar-type='Islamic'></ui5-calendar>
+<ui5-calendar primary-calendar-type='Islamic'></ui5-calendar>
 	
-

Calendar with Buddhist Calendar Type

+

Buddhist Calendar


-		<ui5-calendar primary-calendar-type='Buddhist'></ui5-calendar>
+<ui5-calendar primary-calendar-type='Buddhist'></ui5-calendar>
 	
From a687c2b1d4310ca834307d36a1aadf4c4c45800d Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sun, 29 Nov 2020 04:32:30 +0200 Subject: [PATCH 23/24] - ui5-calendar "selected-dates-change" event detail description is now added in the component metadata. - ui5-calendar selection types are now tested. - ui5-calendar component is marked as new in the playground. --- packages/main/src/Calendar.js | 26 ++++++----- packages/main/test/specs/Calendar.spec.js | 53 +++++++++++++++++++++++ 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js index 4c18bf2f584e..4e94d6ecf3be 100644 --- a/packages/main/src/Calendar.js +++ b/packages/main/src/Calendar.js @@ -36,15 +36,6 @@ import calendarCSS from "./generated/themes/Calendar.css.js"; const metadata = { tag: "ui5-calendar", properties: /** @lends sap.ui.webcomponents.main.Calendar.prototype */ { - /** - * Defines the UNIX timestamp - seconds since 00:00:00 UTC on Jan 1, 1970. - * @type {Integer} - * @private - */ - timestamp: { - type: Integer, - }, - /** * Defines the calendar type used for display. * If not defined, the calendar type of the global configuration is used. @@ -121,6 +112,15 @@ const metadata = { type: Boolean, }, + /** + * Defines the UNIX timestamp - seconds since 00:00:00 UTC on Jan 1, 1970. + * @type {Integer} + * @private + */ + timestamp: { + type: Integer, + }, + _header: { type: Object, }, @@ -155,10 +155,14 @@ const metadata = { /** * Fired when the selected dates changed. * @event sap.ui.webcomponents.main.Calendar#selected-dates-change - * @param {Array} dates The selected dates' timestamps + * @param {Array} dates The selected dates timestamps * @public */ - "selected-dates-change": { type: Array }, + "selected-dates-change": { + detail: { + dates: { type: Array }, + }, + }, }, }; diff --git a/packages/main/test/specs/Calendar.spec.js b/packages/main/test/specs/Calendar.spec.js index dc792f53548d..6f7635c9973a 100644 --- a/packages/main/test/specs/Calendar.spec.js +++ b/packages/main/test/specs/Calendar.spec.js @@ -196,5 +196,58 @@ describe("Calendar general interaction", () => { browser.keys('PageUp'); assert.ok(calendarHeader.shadow$(".ui5-calheader-middlebtn").getAttribute("hidden"), "The button for month is hidden"); + browser.keys("Space"); + }); + + it("Calendar with 'Multiple' selection type", () => { + const calendar = browser.$("#calendar1"); + calendar.setAttribute("selection", "Multiple"); + let selectedDates = browser.execute(() => document.getElementById("calendar1").selectedDates ); + + // deselect previously selected dates + selectedDates.forEach(timestamp => { + const dateDOM = calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="${timestamp}"]`); + dateDOM.click(); + }); + + calendar.setAttribute("timestamp", new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000); + + const dates = [ + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971136000"]`), + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971222400"]`), + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971308800"]`), + ]; + + dates.forEach(date => { + date.click(); + assert.ok(date.hasClass("ui5-dp-item--selected"), `${date.getAttribute("data-sap-timestamp")} is selected`); + }); + + selectedDates = browser.execute(() => document.getElementById("calendar1").selectedDates ); + + assert.deepEqual(selectedDates, [971136000, 971222400, 971308800], "Change event is fired with proper data"); + }); + + it("Calendar with 'Range' selection type", () => { + const calendar = browser.$("#calendar1"); + calendar.setAttribute("timestamp", new Date(Date.UTC(2000, 9, 10, 0, 0, 0)).valueOf() / 1000); + calendar.setAttribute("selection", "Range"); + + const dates = [ + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971740800"]`), + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971827200"]`), + calendar.shadow$("ui5-daypicker").shadow$(`[data-sap-timestamp="971913600"]`), + ]; + + dates[0].click(); + dates[2].click(); + + assert.ok(dates[0].hasClass("ui5-dp-item--selected"), `${dates[0].getAttribute("data-sap-timestamp")} is selected`); + assert.ok(dates[1].hasClass("ui5-dp-item--selected-between"), `${dates[1].getAttribute("data-sap-timestamp")} is selected between`); + assert.ok(dates[2].hasClass("ui5-dp-item--selected"), `${dates[2].getAttribute("data-sap-timestamp")} is selected`); + + const selectedDates = browser.execute(() => document.getElementById("calendar1").selectedDates ); + + assert.deepEqual(selectedDates, [971740800, 971913600], "Change event is fired with proper data"); }); }); From 042b3a8c1f41a30ec56048400ab91fc1a9dfa4b4 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Sun, 29 Nov 2020 04:41:56 +0200 Subject: [PATCH 24/24] - empty tab on the end of a line is deleted. --- packages/playground/build-scripts/samples-prepare.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/playground/build-scripts/samples-prepare.js b/packages/playground/build-scripts/samples-prepare.js index 1475843abf0c..f07f77d8a89f 100644 --- a/packages/playground/build-scripts/samples-prepare.js +++ b/packages/playground/build-scripts/samples-prepare.js @@ -12,6 +12,7 @@ const components = []; // Add new components here const newComponents = [ + "Calendar", "MultiInput", "RangeSlider", "Slider",