Skip to content

Commit 20c55ed

Browse files
feat(ui5-time-picker): improve keyboard handling support (#2092)
PageUp: Hours + 1, PageDown: Hours - 1, PageUp + Shift : Minutes + 1 PageDown + Shift: Minutes - 1, PageUp + Shift + Ctrl: Seconds + 1, PageDown + Shift + Ctrl: Seconds - 1 Related to: #1534
1 parent ed3c393 commit 20c55ed

File tree

4 files changed

+154
-1
lines changed

4 files changed

+154
-1
lines changed

packages/base/src/Keys.js

+18
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ const isBackSpace = event => (event.key ? event.key === "Backspace" : event.keyC
129129

130130
const isDelete = event => (event.key ? event.key === "Delete" : event.keyCode === KeyCodes.DELETE) && !hasModifierKeys(event);
131131

132+
const isPageUp = event => (event.key ? event.key === "PageUp" : event.keyCode === KeyCodes.PAGE_UP) && !hasModifierKeys(event);
133+
134+
const isPageDown = event => (event.key ? event.key === "PageDown" : event.keyCode === KeyCodes.PAGE_DOWN) && !hasModifierKeys(event);
135+
136+
const isPageUpShift = event => (event.key ? event.key === "PageUp" : event.keyCode === KeyCodes.PAGE_UP) && checkModifierKeys(event, false, false, true);
137+
138+
const isPageDownShift = event => (event.key ? event.key === "PageDown" : event.keyCode === KeyCodes.PAGE_DOWN) && checkModifierKeys(event, false, false, true);
139+
140+
const isPageUpShiftCtrl = event => (event.key ? event.key === "PageUp" : event.keyCode === KeyCodes.PAGE_UP) && checkModifierKeys(event, true, false, true);
141+
142+
const isPageDownShiftCtrl = event => (event.key ? event.key === "PageDown" : event.keyCode === KeyCodes.PAGE_DOWN) && checkModifierKeys(event, true, false, true);
143+
132144
const isShow = event => {
133145
if (event.key) {
134146
return isF4(event) || isShowByArrows(event);
@@ -167,4 +179,10 @@ export {
167179
isDelete,
168180
isShow,
169181
isF4,
182+
isPageUp,
183+
isPageDown,
184+
isPageUpShift,
185+
isPageDownShift,
186+
isPageUpShiftCtrl,
187+
isPageDownShiftCtrl,
170188
};

packages/main/src/TimePicker.js

+86
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import {
1414
isTabNext,
1515
isTabPrevious,
1616
isShow,
17+
isPageUp,
18+
isPageDown,
19+
isPageUpShift,
20+
isPageDownShift,
21+
isPageUpShiftCtrl,
22+
isPageDownShiftCtrl,
1723
} from "@ui5/webcomponents-base/src/Keys.js";
1824
import "@ui5/webcomponents-icons/dist/icons/time-entry-request.js";
1925
import PopoverHorizontalAlign from "./types/PopoverHorizontalAlign.js";
@@ -231,6 +237,22 @@ const metadata = {
231237
* For example, if the <code>format-pattern</code> is "HH:mm:ss",
232238
* a valid value string is "11:42:35" and the same is displayed in the input.
233239
*
240+
* <h3>Keyboard handling</h3>
241+
* [F4], [ALT]+[UP], [ALT]+[DOWN] Open/Close picker dialog and move focus to it.
242+
* When closed:
243+
* [PAGEUP] - Increments hours by 1. If 12 am is reached, increment hours to 1 pm and vice versa.
244+
* [PAGEDOWN] - Decrements the corresponding field by 1. If 1 pm is reached, decrement hours to 12 am and vice versa.
245+
* [SHIFT]+[PAGEUP] Increments minutes by 1.
246+
* [SHIFT]+ [PAGEDOWN] Decrements minutes by 1.
247+
* [SHIFT]+[CTRL]+[PAGEUP] Increments seconds by 1.
248+
* [SHIFT]+[CTRL]+ [PAGEDOWN] Decrements seconds by 1.
249+
* When opened:
250+
* [UP] If focus is on one of the selection lists: Select the value which is above the current value. If the first value is selected, select the last value in the list. Exception: AM/ PM List: stay on the first item.
251+
* [DOWN] If focus is on one of the selection lists: Select the value which is below the current value. If the last value is selected, select the first value in the list. Exception: AM/ PM List: stay on the last item.
252+
* [LEFT] If focus is on one of the selection lists: Move focus to the selection list which is left of the current selection list. If focus is at the first selection list, move focus to the last selection list.
253+
* [RIGHT] If focus is on one of the selection lists: Move focus to the selection list which is right of the current selection list. When focus is at the last selection list, move focus to the first selection list.
254+
* [PAGEUP] If focus is on one of the selection lists: Move focus to the first entry of this list.
255+
* [PAGEDOWN] If focus is on one of the selection lists: Move focus to the last entry of this list.
234256
* <h3>ES6 Module Import</h3>
235257
*
236258
* <code>import @ui5/webcomponents/dist/TimePicker.js";</code>
@@ -647,6 +669,26 @@ class TimePicker extends UI5Element {
647669
e.preventDefault();
648670
responsivePopover.querySelector(`.ui5-time-picker-footer`).lastElementChild.focus();
649671
}
672+
673+
if (isPageDown(e)) {
674+
this._selectLimitCell(e, false);
675+
} else if (isPageUp(e)) {
676+
this._selectLimitCell(e, true);
677+
}
678+
}
679+
680+
_selectLimitCell(e, isMax) {
681+
e.preventDefault();
682+
if (e.target === this.hoursSlider) {
683+
const hoursArray = this.hoursArray;
684+
e.target.value = isMax ? hoursArray[hoursArray.length - 1] : hoursArray[0];
685+
} else if (e.target === this.minutesSlider) {
686+
const minutesArray = this.minutesArray;
687+
e.target.value = isMax ? minutesArray[minutesArray.length - 1] : minutesArray[0];
688+
} else if (e.target === this.secondsSlider) {
689+
const secondsArray = this.secondsArray;
690+
e.target.value = isMax ? secondsArray[secondsArray.length - 1] : secondsArray[0];
691+
}
650692
}
651693

652694
_onfooterkeydown(e) {
@@ -674,6 +716,50 @@ class TimePicker extends UI5Element {
674716
e.preventDefault();
675717
this.togglePicker();
676718
}
719+
720+
if (this.isOpen()) {
721+
return;
722+
}
723+
724+
if (isPageUpShiftCtrl(e)) {
725+
e.preventDefault();
726+
this._incrementValue(true, false, false, true);
727+
} else if (isPageUpShift(e)) {
728+
e.preventDefault();
729+
this._incrementValue(true, false, true, false);
730+
} else if (isPageUp(e)) {
731+
e.preventDefault();
732+
this._incrementValue(true, true, false, false);
733+
}
734+
735+
if (isPageDownShiftCtrl(e)) {
736+
e.preventDefault();
737+
this._incrementValue(false, false, false, true);
738+
} else if (isPageDownShift(e)) {
739+
e.preventDefault();
740+
this._incrementValue(false, false, true, false);
741+
} else if (isPageDown(e)) {
742+
e.preventDefault();
743+
this._incrementValue(false, true, false, false);
744+
}
745+
}
746+
747+
_incrementValue(increment, hours, minutes, seconds) {
748+
const date = this.dateValue;
749+
const incrementStep = increment ? 1 : -1;
750+
751+
if (hours && this.shouldBuildHoursSlider) {
752+
date.setHours(date.getHours() + incrementStep);
753+
} else if (minutes && this.shouldBuildMinutesSlider) {
754+
date.setMinutes(date.getMinutes() + incrementStep);
755+
} else if (seconds && this.shouldBuildSecondsSlider) {
756+
date.setSeconds(date.getSeconds() + incrementStep);
757+
} else {
758+
return;
759+
}
760+
761+
this.setValue(this.formatValue(date));
762+
this.fireEvent("change", { value: this.value, valid: true });
677763
}
678764

679765
_handleWheel(e) {

packages/main/src/TimePickerPopover.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@keydown="{{_ontimepickerpopoverkeydown}}"
1414
@wheel="{{_handleWheel}}"
1515
>
16-
<div class="{{classes.container}}" @keydown={{_oncontainerkeydown}} tabindex="0" @focusin="{{_onfocuscontainerin}}">
16+
<div class="{{classes.container}}" @keydown={{_oncontainerkeydown}} tabindex="-1" @focusin="{{_onfocuscontainerin}}">
1717
{{#if shouldBuildHoursSlider}}
1818
<ui5-wheelslider
1919
label = "{{hoursSliderTitle}}"

packages/main/test/specs/TimePicker.spec.js

+49
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,53 @@ describe("TimePicker general interaction", () => {
123123
// assert
124124
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("valueState"), "None", "The value state is None");
125125
});
126+
127+
it("tests input keyboard handling", () => {
128+
const timepicker = browser.$("#timepicker5");
129+
130+
// act
131+
timepicker.click();
132+
timepicker.keys(['Shift', 'PageUp']);
133+
timepicker.keys('Shift');
134+
135+
// assert
136+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "12:01:01", "The value of minutes is +1");
137+
// act
138+
timepicker.click();
139+
timepicker.keys(['Shift', 'PageDown']);
140+
timepicker.keys('Shift');
141+
142+
// assert
143+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "12:00:01", "The value of minutes is -1");
144+
145+
// act
146+
timepicker.click();
147+
timepicker.keys('PageUp');
148+
149+
// assert
150+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "01:00:01", "The value of hours is +1");
151+
// act
152+
timepicker.click();
153+
timepicker.keys('PageDown');
154+
155+
// assert
156+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "12:00:01", "The value of hours is -1");
157+
158+
// act
159+
timepicker.click();
160+
timepicker.keys(['Shift', 'Control', 'PageUp']);
161+
timepicker.keys('Shift');
162+
timepicker.keys('Control');
163+
164+
// assert
165+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "12:00:02", "The value of seconds is +1");
166+
// act
167+
timepicker.click();
168+
timepicker.keys(['Shift', 'Control', 'PageDown']);
169+
timepicker.keys('Shift');
170+
timepicker.keys('Control');
171+
172+
// assert
173+
assert.strictEqual(timepicker.shadow$("ui5-input").getProperty("value"), "12:00:01", "The value of seconds is -1");
174+
});
126175
});

0 commit comments

Comments
 (0)