Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-daterange-picker): keyboard handling improvement #2179

Merged
merged 22 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
507d9a4
fix(ui5-daterange-picker): keyboard handling improvement
unazko Sep 4, 2020
d830138
feat(ui5-daterange-picker): documentation added for keyboard handling
unazko Sep 4, 2020
f470914
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 8, 2020
2af45ff
feat(ui5-daterange-picker): documentation added for keyboard handling
unazko Sep 8, 2020
8f8291c
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 18, 2020
dc96cde
JS documentation is now accurate and helper function,
unazko Sep 18, 2020
b133650
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 21, 2020
b2c777f
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 23, 2020
3ac69b2
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 23, 2020
8e5eec9
fix(ui5-daterange-picker): keyboard handling improvement
unazko Sep 23, 2020
bfdfdf2
feat(ui5-daterange-picker): keyboard handling improvement
unazko Sep 24, 2020
064c017
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 30, 2020
05e9c79
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Sep 30, 2020
c0df5ef
fix(ui5-daterange-picker): keyboard handling improvement
unazko Oct 1, 2020
db3da35
feat(ui5-daterange-picker): keyboard handling improvement
unazko Oct 1, 2020
29d0021
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Oct 1, 2020
be5b7ff
feat(ui5-daterange-picker): keyboard handling improvement
unazko Oct 1, 2020
5980803
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Oct 1, 2020
f813c41
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Oct 2, 2020
366545d
Merge branch 'master' of https://github.com/unazko/ui5-webcomponents …
unazko Oct 5, 2020
c8bb02f
Unnecessary calls to the CalendarDate instances are now removed.
unazko Oct 5, 2020
ed2f49c
CalendarDate intances created with "CalendarDate.fromTimestamp" method
unazko Oct 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions packages/main/src/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,39 +532,54 @@ class DatePicker extends UI5Element {

if (isPageUpShiftCtrl(event)) {
event.preventDefault();
this._changeDateValue(true, true, false, false);
this._changeDateValueWrapper(true, true, false, false);
} else if (isPageUpShift(event)) {
event.preventDefault();
this._changeDateValue(true, false, true, false);
this._changeDateValueWrapper(true, false, true, false);
} else if (isPageUp(event)) {
event.preventDefault();
this._changeDateValue(true, false, false, true);
this._changeDateValueWrapper(true, false, false, true);
}

if (isPageDownShiftCtrl(event)) {
event.preventDefault();
this._changeDateValue(false, true, false, false);
this._changeDateValueWrapper(false, true, false, false);
} else if (isPageDownShift(event)) {
event.preventDefault();
this._changeDateValue(false, false, true, false);
this._changeDateValueWrapper(false, false, true, false);
} else if (isPageDown(event)) {
event.preventDefault();
this._changeDateValue(false, false, false, true);
this._changeDateValueWrapper(false, false, false, true);
}
}

/**
* Adds or extracts a given number of measuring units from the "dateValue" property value
*
* @param {boolean} forward if true indicates addition
* @param {boolean} years indicates that the measuring unit is in years
* @param {boolean} months indicates that the measuring unit is in months
* @param {boolean} days indicates that the measuring unit is in days
* @param {boolean} forward if true indicates addition
* @param {int} step number of measuring units to substract or add defaults to 1
*/
_changeDateValue(forward, years, months, days, step = 1) {
_changeDateValueWrapper(forward, years, months, days, step = 1) {
let date = this.dateValue;
date = this._changeDateValue(date, forward, years, months, days, step);
this.value = this.formatValue(date);
this.fireEvent("change", { value: this.value, valid: true });
}

/**
* Adds or extracts a given number of measuring units from the "dateValue" property value
*
* @param {boolean} date js date object to be changed
* @param {boolean} years indicates that the measuring unit is in years
* @param {boolean} months indicates that the measuring unit is in months
* @param {boolean} days indicates that the measuring unit is in days
* @param {boolean} forward if true indicates addition
unazko marked this conversation as resolved.
Show resolved Hide resolved
* @param {int} step number of measuring units to substract or add defaults ot 1
* @returns {Object} JS date object
*/
_changeDateValue(date, forward, years, months, days, step = 1) {
if (!date) {
return;
}
Expand All @@ -576,6 +591,10 @@ class DatePicker extends UI5Element {
return;
}

if (incrementStep === 0 || (!days && !months && !years)) {
return;
}

if (days) {
date.setDate(date.getDate() + incrementStep);
} else if (months) {
Expand All @@ -594,8 +613,6 @@ class DatePicker extends UI5Element {
// day doesn't exist in this month (February 29th)
date.setDate(0);
}
} else {
return;
}

if (date.valueOf() < this._minDate) {
Expand All @@ -604,8 +621,7 @@ class DatePicker extends UI5Element {
date = new Date(this._maxDate);
}

this.value = this.formatValue(date);
this.fireEvent("change", { value: this.value, valid: true });
return date;
}

_toggleAndFocusInput() {
Expand Down
109 changes: 109 additions & 0 deletions packages/main/src/DateRangePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";
import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js";
import DateRangePickerTemplate from "./generated/templates/DateRangePickerTemplate.lit.js";
import RenderScheduler from "../../base/src/RenderScheduler.js";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks several builds, please use the "@ui5/webcomponents-base/dist/RenderScheduler.js" form

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will for the future. Thanks.


// Styles
import DateRangePickerCss from "./generated/themes/DateRangePicker.css.js";
Expand Down Expand Up @@ -64,6 +65,25 @@ const metadata = {
*
* <code>import @ui5/webcomponents/dist/DateRangePicker.js";</code>
*
* <h3>Keyboard Handling</h3>
* The <code>ui5-daterange-picker</code> provides advanced keyboard handling.
* <br>
*
* When the <code>ui5-daterange-picker</code> input field is focused the user can
* increment or decrement the corresponding field of the JS date object referenced by <code>_firstDateTimestamp</code> propery
* if the caret symbol is before the delimiter character or <code>_lastDateTimestamp</code> property if the caret symbol is
* after the delimiter character.
* The following shortcuts are enabled:
* <br>
* <ul>
* <li>[PAGEDOWN] - Decrements the corresponding day of the month by one</li>
* <li>[SHIFT] + [PAGEDOWN] - Decrements the corresponding month by one</li>
* <li>[SHIFT] + [CTRL] + [PAGEDOWN] - Decrements the corresponding year by one</li>
* <li>[PAGEUP] - Increments the corresponding day of the month by one</li>
* <li>[SHIFT] + [PAGEUP] - Increments the corresponding month by one</li>
* <li>[SHIFT] + [CTRL] + [PAGEUP] - Increments the corresponding year by one</li>
* </ul>
*
* @constructor
* @author SAP SE
* @alias sap.ui.webcomponents.main.DateRangePicker
Expand Down Expand Up @@ -370,6 +390,95 @@ class DateRangePicker extends DatePicker {
}
}

/**
* Adds or extracts a given number of measuring units from the "dateValue" property value
*
* @param {boolean} forward if true indicates addition
* @param {boolean} years indicates that the measuring unit is in years
* @param {boolean} months indicates that the measuring unit is in months
* @param {boolean} days indicates that the measuring unit is in days
* @param {int} step number of measuring units to substract or add defaults ot 1
*/
async _changeDateValueWrapper(forward, years, months, days, step = 1) {
const emptyValue = this.value === "";
const isValid = emptyValue || this._checkValueValidity(this.value);

if (!isValid) {
return;
}

const dates = this._splitValueByDelimiter(this.value);
const innerInput = this.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
const caretPos = this._getCaretPosition(innerInput);
const first = dates[0] && caretPos <= dates[0].trim().length + 1;
const last = dates[1] && (caretPos >= this.value.length - dates[1].trim().length - 1 && caretPos <= this.value.length);
let firstDate = this.getFormat().parse(dates[0]);
let lastDate = this.getFormat().parse(dates[1]);

if (first && firstDate) {
firstDate = this._changeDateValue(firstDate, forward, years, months, days, step);
} else if (last && lastDate) {
lastDate = this._changeDateValue(lastDate, forward, years, months, days, step);
}

if (firstDate > lastDate) {
const temp = firstDate;
firstDate = lastDate;
lastDate = temp;
}

const newValue = this._formatValue(firstDate.valueOf() / 1000, lastDate.valueOf() / 1000);

this._setValue(newValue);
await RenderScheduler.whenFinished();

// Return the carent on the previous position after rendering
this._setCaretPosition(innerInput, caretPos);

this.fireEvent("change", { value: newValue, valid: isValid });
}

/**
* Returns the caret (cursor) position of the specified text field (field).
* Return value range is 0-field.value.length.
*/
_getCaretPosition(field) {
// Initialize
let caretPos = 0;

// IE Support
if (document.selection) {
// Set focus on the element
field.focus();

// To get cursor position, get empty selection range
const selection = document.selection.createRange();

// Move selection start to 0 position
selection.moveStart("character", -field.value.length);

// The caret position is selection length
caretPos = selection.text.length;
} else if (field.selectionStart || field.selectionStart === "0") { // Firefox support
caretPos = field.selectionDirection === "backward" ? field.selectionStart : field.selectionEnd;
}

return caretPos;
}

_setCaretPosition(field, caretPos) {
if (field.createTextRange) {
const range = field.createTextRange();
range.move("character", caretPos);
range.select();
} else if (field.selectionStart) {
field.focus();
field.setSelectionRange(caretPos, caretPos);
} else {
field.focus();
}
}

_handleCalendarSelectedDatesChange() {
this._updateValueCalendarSelectedDatesChange();
this._cleanHoveredAttributeFromVisibleItems();
Expand Down
12 changes: 8 additions & 4 deletions packages/main/src/DayPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,7 @@ class DayPicker extends UI5Element {
}

onAfterRendering() {
if (this.selectedDates.length === 1) {
this.fireEvent("daypickerrendered", { focusedItemIndex: this._itemNav.currentIndex });
}
this._fireDayPickerRendered();
}

_onmousedown(event) {
Expand Down Expand Up @@ -780,8 +778,14 @@ class DayPicker extends UI5Element {

const newItemIndex = this._itemNav._getItems().findIndex(item => parseInt(item.timestamp) === timestamp);
this._itemNav.currentIndex = newItemIndex;

this._itemNav.focusCurrent();
this._fireDayPickerRendered();
}

_fireDayPickerRendered() {
if (this.selectedDates.length === 1) {
this.fireEvent("daypickerrendered", { focusedItemIndex: this._itemNav.currentIndex });
}
}

_isWeekend(oDate) {
Expand Down
2 changes: 1 addition & 1 deletion packages/main/test/pages/DateRangePicker.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ <h3>daterange-picker with minDate 01/09/2019 and maxDate 01/11/2019</h3>
<section class="ui5-content-density-compact">
<h3>daterange-picker in Compact</h3>
<div>
<ui5-daterange-picker delimiter="@"></ui5-daterange-picker>
<ui5-daterange-picker id="daterange-picker5" delimiter="@"></ui5-daterange-picker>
</div>
</section>

Expand Down
90 changes: 90 additions & 0 deletions packages/main/test/specs/DateRangePicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,94 @@ describe("DateRangePicker general interaction", () => {

assert.strictEqual(browser.$("#labelChange").getHTML(false), "1", "The change event was fired once");
});

it("Page up/down increments/decrements day value", () => {
const dateRange = browser.$("#daterange-picker5");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setValue("Jul 16, 2020 @ Jul 29, 2020");
innerInput.click();
dateRange._setCaretPosition(innerInput, 15);
});

browser.keys('PageDown');
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 28, 2020");

browser.keys('PageUp');
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setCaretPosition(innerInput, 5);
});

browser.keys('PageDown');
assert.strictEqual(dateRange.getProperty("value"), "Jul 15, 2020 @ Jul 29, 2020");

browser.keys('PageUp');
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");
});

it("Page up/down increments/decrements month value", () => {
const dateRange = browser.$("#daterange-picker5");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setValue("Jul 16, 2020 @ Jul 29, 2020");
innerInput.click();
dateRange._setCaretPosition(innerInput, 15);
});

browser.keys(['Shift', 'PageUp']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Aug 29, 2020");

browser.keys(['Shift', 'PageDown']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setCaretPosition(innerInput, 5);
});

browser.keys(['Shift', 'PageDown']);
assert.strictEqual(dateRange.getProperty("value"), "Jun 16, 2020 @ Jul 29, 2020");

browser.keys(['Shift', 'PageUp']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");
});

it("Page up/down increments/decrements year value", () => {
const dateRange = browser.$("#daterange-picker5");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setValue("Jul 16, 2020 @ Jul 29, 2020");
innerInput.click();
dateRange._setCaretPosition(innerInput, 15);
});

browser.keys(['Control', 'Shift', 'PageUp']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2021");

browser.keys(['Control', 'Shift', 'PageDown']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");

browser.execute(() => {
const dateRange = document.getElementById("daterange-picker5");
const innerInput = dateRange.shadowRoot.querySelector("ui5-input").shadowRoot.querySelector(".ui5-input-inner");
dateRange._setCaretPosition(innerInput, 5);
});

browser.keys(['Control', 'Shift', 'PageDown']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2019 @ Jul 29, 2020");

browser.keys(['Control', 'Shift', 'PageUp']);
assert.strictEqual(dateRange.getProperty("value"), "Jul 16, 2020 @ Jul 29, 2020");
});
});