Skip to content

Commit

Permalink
Refactor time-picker to be more keyboard friendly & refactor test hel…
Browse files Browse the repository at this point in the history
…pers

This changes the time-picker to work more like the date-picker. The trigger is now a simple button, where the input is in the dropdown - similar to ember-power-select. This makes a lot of the focus handling much easier.
  • Loading branch information
Francesco Novy committed Oct 25, 2018
1 parent 827eed2 commit e90518f
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 432 deletions.
23 changes: 17 additions & 6 deletions addon-test-support/helpers/date-picker.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { click, triggerKeyEvent, fillIn, find } from '@ember/test-helpers';
import { click, find } from '@ember/test-helpers';
import moment from 'moment';
import { assert } from '@ember/debug';
import { deprecate } from '@ember/application/deprecations';
import { selectTime } from './time-picker';

export function getDatePicker(element) {
if (typeof element === 'string') {
Expand Down Expand Up @@ -73,18 +75,27 @@ export function getDatePicker(element) {
};
}

export async function setTimePickerValue(el, val) {
let timeInput = el.querySelector('input');
await fillIn(timeInput, val);
await triggerKeyEvent(timeInput, 'keyup', 13); // Enter event
export async function setTimePickerValue() {
deprecate('`setTimePickerValue` test helper has been deprecated in favor of `import { selectTime } from "ember-date-components/test-support/helpers/time-picker"`, please use it instead.', false, {
id: 'ember-date-components.test-support.helpers.date-picker',
until: '3.0.0'
});

return selectTime(...arguments);
}

export async function selectDate(element, date) {
assert('date must be momentjs object', moment.isMoment(date));
assert('date must be moment.js object', moment.isMoment(date));

let datePicker = await getDatePicker(element);
await datePicker.toggle();
await datePicker.selectDate(date);
}

export async function selectDateTime(element, date) {
assert('date must be moment.js object', moment.isMoment(date));
await selectDate(element, date);
await selectTime(element, date);
}

export default getDatePicker;
48 changes: 48 additions & 0 deletions addon-test-support/helpers/time-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { click, triggerKeyEvent, fillIn, find } from '@ember/test-helpers';
import moment from 'moment';
import { assert } from '@ember/debug';
import parseTime from 'ember-date-components/utils/parse-time';

export async function selectTime(element, val) {
if (typeof element === 'string') {
element = find(element);
}

assert('selectTime is passed a DOM node or a matching selector string', !!element);

let timePickerButton = element.hasAttribute('data-time-picker-toggle-button')
? element
: element.querySelector('[data-time-picker-toggle-button]');

let elementId = timePickerButton.getAttribute('data-time-picker-toggle-button');

await click(timePickerButton);

if (moment.isMoment(val)) {
val = val.format('HH:mm');
}

let timeInput = find(`[data-time-picker-input-instance="${elementId}"]`);
await fillIn(timeInput, val);
await triggerKeyEvent(timeInput, 'keydown', 'Enter'); // Enter event
}

export function getSelectedTime(element) {
if (typeof element === 'string') {
element = find(element);
}

assert('getSelectedTime is passed a DOM node or a matching selector string', !!element);

let timePickerButton = element.hasAttribute('data-time-picker-toggle-button')
? element
: element.querySelector('[data-time-picker-toggle-button]');

let timeValue = timePickerButton.getAttribute('data-time-picker-value');
return timeValue ? parseTime(timeValue) : null;
}

export default {
selectTime,
getSelectedTime
};
92 changes: 35 additions & 57 deletions addon/components/time-picker-input.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get, computed } from '@ember/object';
import { get } from '@ember/object';
import TextField from '@ember/component/text-field';
import { once } from '@ember/runloop';

Expand All @@ -15,10 +15,12 @@ import { once } from '@ember/runloop';
export default TextField.extend({
classNames: [],

attributeBindings: ['disabled'],
attributeBindings: ['disabled', 'data-time-picker-input-instance', 'tabindex'],

type: 'text',

tabindex: -1,

/**
* If this is true, the time picker is disabled and the selected time cannot be changed.
*
Expand All @@ -29,83 +31,59 @@ export default TextField.extend({
*/
disabled: false,

KEY_EVENTS: computed(function() {
return {
38: 'arrowUp',
40: 'arrowDown',
13: 'enter',
27: 'escape'
};
}),

interpretKeyEvents(event) {
if (!event) {
return this.inputChange();
}
let map = get(this, 'KEY_EVENTS');
let { keyCode } = event;
keyUp() {
// overwrite default implementation
},

let method = map[keyCode];
if (method) {
return this[method](event);
} else {
return once(this, this.inputChange);
keyDown(event) {
// Tab doesn't trigger keyUp, so we need to capture it in keyDown
switch (event.key) {
case 'Enter':
return this._enter(event);
case 'Escape':
return this._escape(event);
case 'ArrowUp':
return this._arrowUp(event);
case 'ArrowDown':
return this._arrowDown(event);
case 'Tab':
return this._tab(event);
}
},

change() {
input() {
once(this, this.inputChange);
},

inputChange() {
this._elementValueDidChange();
let value = get(this, 'value');

let action = get(this, 'input-change');
if (action && typeof action === 'function') {
return action(value, this);
} else {
console.warn('input-change action on time-picker-input needs to be a closure action.'); // eslint-disable-line
}
return action(value, this);
},

arrowUp(event) {
let action = get(this, 'arrow-up');
_tab(event) {
let action = get(this, 'tab');
return action(this, event);
},

if (action && typeof action === 'function') {
return action(this, event);
} else {
console.warn('arrow-up action on time-picker-input needs to be a closure action.'); // eslint-disable-line
}
_arrowUp(event) {
let action = get(this, 'arrow-up');
return action(this, event);
},

arrowDown(event) {
_arrowDown(event) {
let action = get(this, 'arrow-down');

if (action && typeof action === 'function') {
return action(this, event);
} else {
console.warn('arrow-down action on time-picker-input needs to be a closure action.'); // eslint-disable-line
}
return action(this, event);
},

escape(event) {
_escape(event) {
let action = get(this, 'escape');

if (action && typeof action === 'function') {
return action(this, event);
} else {
console.warn('escape action on time-picker-input needs to be a closure action.'); // eslint-disable-line
}
return action(this, event);
},

enter(event) {
_enter(event) {
let action = get(this, 'enter');

if (action && typeof action === 'function') {
return action(this, event);
} else {
console.warn('enter action on time-picker-input needs to be a closure action.'); // eslint-disable-line
}
return action(this, event);
}
});
Loading

0 comments on commit e90518f

Please sign in to comment.