From b090e2e1a4d2ad0be7ca33f450f19051f96d1947 Mon Sep 17 00:00:00 2001 From: smhigley Date: Tue, 9 Oct 2018 13:09:37 -0700 Subject: [PATCH 1/2] include focus mixin in focusable widgets --- src/checkbox/index.ts | 6 +- src/checkbox/tests/unit/Checkbox.ts | 6 + src/listbox/index.ts | 35 +-- src/listbox/tests/unit/Listbox.ts | 3 + src/radio/index.ts | 6 +- src/radio/tests/unit/Radio.ts | 1 + src/select/index.ts | 25 +- src/select/tests/unit/Select.ts | 62 ++++- src/slider/index.ts | 6 +- src/slider/tests/unit/Slider.ts | 7 + src/tab-controller/TabButton.ts | 19 +- src/tab-controller/index.ts | 13 +- src/tab-controller/tests/unit/TabButton.ts | 17 +- .../tests/unit/TabController.ts | 249 ++---------------- src/text-area/index.ts | 6 +- src/text-area/tests/unit/Textarea.ts | 1 + src/time-picker/index.ts | 7 +- src/time-picker/tests/unit/TimePicker.ts | 5 + src/title-pane/index.ts | 6 +- src/title-pane/tests/unit/TitlePane.ts | 1 + 20 files changed, 165 insertions(+), 316 deletions(-) diff --git a/src/checkbox/index.ts b/src/checkbox/index.ts index 7485bf9f7a..5ba9ccf050 100644 --- a/src/checkbox/index.ts +++ b/src/checkbox/index.ts @@ -1,6 +1,7 @@ import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase'; import { DNode } from '@dojo/framework/widget-core/interfaces'; import { ThemedMixin, ThemedProperties, theme } from '@dojo/framework/widget-core/mixins/Themed'; +import { FocusMixin, FocusProperties } from '@dojo/framework/widget-core/mixins/Focus'; import Focus from '@dojo/framework/widget-core/meta/Focus'; import Label from '../label/index'; import { CustomAriaProperties, LabeledProperties, InputProperties, CheckboxRadioEventProperties, KeyEventProperties, PointerEventProperties } from '../common/interfaces'; @@ -21,7 +22,7 @@ import { customElement } from '@dojo/framework/widget-core/decorators/customElem * @property onLabel Label to show in the "on" positin of a toggle * @property value The current value */ -export interface CheckboxProperties extends ThemedProperties, InputProperties, LabeledProperties, KeyEventProperties, PointerEventProperties, CustomAriaProperties, CheckboxRadioEventProperties { +export interface CheckboxProperties extends ThemedProperties, InputProperties, FocusProperties, LabeledProperties, KeyEventProperties, PointerEventProperties, CustomAriaProperties, CheckboxRadioEventProperties { checked?: boolean; mode?: Mode; offLabel?: DNode; @@ -37,7 +38,7 @@ export enum Mode { toggle = 'toggle' } -export const ThemedBase = ThemedMixin(WidgetBase); +export const ThemedBase = ThemedMixin(FocusMixin(WidgetBase)); @theme(css) @customElement({ @@ -189,6 +190,7 @@ export class CheckboxBase

ext classes: this.theme(css.input), checked, disabled, + focus: this.shouldFocus, 'aria-invalid': invalid === true ? 'true' : null, name, readOnly, diff --git a/src/checkbox/tests/unit/Checkbox.ts b/src/checkbox/tests/unit/Checkbox.ts index b252e53b9b..6c56c0ed1e 100644 --- a/src/checkbox/tests/unit/Checkbox.ts +++ b/src/checkbox/tests/unit/Checkbox.ts @@ -56,6 +56,7 @@ const expected = function(label = false, toggle = false, toggleLabels = false, c classes: css.input, checked, disabled: undefined, + focus: noop, 'aria-invalid': null, name: undefined, readOnly: undefined, @@ -119,6 +120,7 @@ registerSuite('Checkbox', { classes: css.input, checked: true, disabled: undefined, + focus: noop, 'aria-invalid': null, readOnly: undefined, 'aria-readonly': null, @@ -168,6 +170,7 @@ registerSuite('Checkbox', { id: '', classes: css.input, checked: false, + focus: noop, 'aria-invalid': 'true', 'aria-readonly': 'true', type: 'checkbox', @@ -203,6 +206,7 @@ registerSuite('Checkbox', { id: '', classes: css.input, checked: false, + focus: noop, 'aria-invalid': null, 'aria-readonly': null, type: 'checkbox', @@ -252,6 +256,7 @@ registerSuite('Checkbox', { v('input', { disabled: true, classes: css.input, + focus: noop, 'aria-invalid': 'true', readOnly: true, 'aria-readonly': 'true', @@ -307,6 +312,7 @@ registerSuite('Checkbox', { classes: css.input, checked: false, disabled: undefined, + focus: noop, 'aria-invalid': null, name: undefined, readOnly: undefined, diff --git a/src/listbox/index.ts b/src/listbox/index.ts index 838feb0278..8255d94a4e 100644 --- a/src/listbox/index.ts +++ b/src/listbox/index.ts @@ -6,6 +6,7 @@ import { CustomAriaProperties } from '../common/interfaces'; import { formatAriaProperties, Keys } from '../common/util'; import MetaBase from '@dojo/framework/widget-core/meta/Base'; import { ThemedMixin, ThemedProperties, theme } from '@dojo/framework/widget-core/mixins/Themed'; +import { FocusMixin, FocusProperties } from '@dojo/framework/widget-core/mixins/Focus'; import { uuid } from '@dojo/framework/core/util'; import { v, w } from '@dojo/framework/widget-core/d'; import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase'; @@ -45,14 +46,13 @@ export class ScrollMeta extends MetaBase { * @property onOptionSelect Called with the option data of the new requested selected item */ -export interface ListboxProperties extends ThemedProperties, CustomAriaProperties { +export interface ListboxProperties extends ThemedProperties, FocusProperties, CustomAriaProperties { activeIndex?: number; getOptionDisabled?(option: any, index: number): boolean; getOptionId?(option: any, index: number): string; getOptionLabel?(option: any, index: number): DNode; getOptionSelected?(option: any, index: number): boolean; widgetId?: string; - focus?: boolean; multiselect?: boolean; optionData?: any[]; tabIndex?: number; @@ -62,7 +62,7 @@ export interface ListboxProperties extends ThemedProperties, CustomAriaPropertie onOptionSelect?(option: any, index: number, key?: string | number): void; } -export const ThemedBase = ThemedMixin(WidgetBase); +export const ThemedBase = ThemedMixin(FocusMixin(WidgetBase)); @theme(css) @diffProperty('optionData', reference) @@ -70,7 +70,6 @@ export const ThemedBase = ThemedMixin(WidgetBase); tag: 'dojo-listbox', properties: [ 'activeIndex', - 'focus', 'multiselect', 'tabIndex', 'visualFocus', @@ -239,27 +238,21 @@ export class ListboxBase

extend aria = {}, widgetId, multiselect = false, - focus, tabIndex = 0 } = this.properties; const themeClasses = this.getModifierClasses(); - return v('div', () => { - if (focus) { - this.meta(Focus).set('root'); - } - - return { - ...formatAriaProperties(aria), - 'aria-activedescendant': this._getOptionId(activeIndex), - 'aria-multiselectable': multiselect ? 'true' : null, - classes: this.theme([ css.root, ...themeClasses ]), - id: widgetId, - key: 'root', - role: 'listbox', - tabIndex, - onkeydown: this._onKeyDown - }; + return v('div', { + ...formatAriaProperties(aria), + 'aria-activedescendant': this._getOptionId(activeIndex), + 'aria-multiselectable': multiselect ? 'true' : null, + classes: this.theme([ css.root, ...themeClasses ]), + id: widgetId, + focus: this.shouldFocus, + key: 'root', + role: 'listbox', + tabIndex, + onkeydown: this._onKeyDown }, this.renderOptions()); } } diff --git a/src/listbox/tests/unit/Listbox.ts b/src/listbox/tests/unit/Listbox.ts index f511238f47..b6715ea52f 100644 --- a/src/listbox/tests/unit/Listbox.ts +++ b/src/listbox/tests/unit/Listbox.ts @@ -109,6 +109,7 @@ const expectedVdom = function(options: DNode[] = []) { 'aria-multiselectable': null, classes: [ css.root, null ], id: undefined, + focus: noop, key: 'root', role: 'listbox', tabIndex: 0, @@ -153,6 +154,7 @@ registerSuite('Listbox', { classes: [ css.root, css.focused ], id: 'bar', tabIndex: -1, + focus: noop, key: 'root', role: 'listbox', onkeydown: noop @@ -192,6 +194,7 @@ registerSuite('Listbox', { 'aria-multiselectable': null, classes: [ css.root, css.focused ], id: undefined, + focus: noop, key: 'root', role: 'listbox', tabIndex: 0, diff --git a/src/radio/index.ts b/src/radio/index.ts index c74db9037b..2f419f41f9 100644 --- a/src/radio/index.ts +++ b/src/radio/index.ts @@ -1,6 +1,7 @@ import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase'; import { DNode } from '@dojo/framework/widget-core/interfaces'; import { ThemedMixin, ThemedProperties, theme } from '@dojo/framework/widget-core/mixins/Themed'; +import { FocusMixin, FocusProperties } from '@dojo/framework/widget-core/mixins/Focus'; import Focus from '@dojo/framework/widget-core/meta/Focus'; import Label from '../label/index'; import { CustomAriaProperties, LabeledProperties, InputProperties, KeyEventProperties, CheckboxRadioEventProperties, PointerEventProperties } from '../common/interfaces'; @@ -18,12 +19,12 @@ import { customElement } from '@dojo/framework/widget-core/decorators/customElem * @property checked Checked/unchecked property of the radio * @property value The current value */ -export interface RadioProperties extends ThemedProperties, LabeledProperties, InputProperties, KeyEventProperties, PointerEventProperties, CustomAriaProperties, CheckboxRadioEventProperties { +export interface RadioProperties extends ThemedProperties, LabeledProperties, InputProperties, FocusProperties, KeyEventProperties, PointerEventProperties, CustomAriaProperties, CheckboxRadioEventProperties { checked?: boolean; value?: string; } -export const ThemedBase = ThemedMixin(WidgetBase); +export const ThemedBase = ThemedMixin(FocusMixin(WidgetBase)); @theme(css) @customElement({ @@ -146,6 +147,7 @@ export class RadioBase

extends Them classes: this.theme(css.input), checked, disabled, + focus: this.shouldFocus, 'aria-invalid': invalid === true ? 'true' : null, name, readOnly, diff --git a/src/radio/tests/unit/Radio.ts b/src/radio/tests/unit/Radio.ts index d1fc6cf8be..dcb2dd1a48 100644 --- a/src/radio/tests/unit/Radio.ts +++ b/src/radio/tests/unit/Radio.ts @@ -36,6 +36,7 @@ const expected = function({ label = false, rootOverrides = {}, inputOverrides = classes: css.input, checked: false, disabled: disabled, + focus: noop, 'aria-invalid': invalid ? 'true' : null, name: undefined, readOnly: readOnly, diff --git a/src/select/index.ts b/src/select/index.ts index 494e36633a..bc1cfa13c9 100644 --- a/src/select/index.ts +++ b/src/select/index.ts @@ -3,6 +3,7 @@ import { diffProperty } from '@dojo/framework/widget-core/decorators/diffPropert import { reference } from '@dojo/framework/widget-core/diff'; import { DNode } from '@dojo/framework/widget-core/interfaces'; import { ThemedMixin, ThemedProperties, theme } from '@dojo/framework/widget-core/mixins/Themed'; +import { FocusMixin, FocusProperties } from '@dojo/framework/widget-core/mixins/Focus'; import Focus from '@dojo/framework/widget-core/meta/Focus'; import { v, w } from '@dojo/framework/widget-core/d'; import { uuid } from '@dojo/framework/core/util'; @@ -31,7 +32,7 @@ import { customElement } from '@dojo/framework/widget-core/decorators/customElem * @property useNativeElement Use the native element if true * @property value The current value */ -export interface TimePickerProperties extends ThemedProperties, InputProperties, LabeledProperties { +export interface TimePickerProperties extends ThemedProperties, FocusProperties, InputProperties, LabeledProperties { autoBlur?: boolean; clearable?: boolean; end?: string; @@ -155,7 +156,7 @@ export function parseUnits (value: string | TimeUnits): TimeUnits { return value; } -export const ThemedBase = ThemedMixin(WidgetBase); +export const ThemedBase = ThemedMixin(FocusMixin(WidgetBase)); @theme(css) @customElement({ @@ -315,6 +316,7 @@ export class TimePickerBase

({ @@ -144,6 +145,7 @@ export class TitlePaneBase

'aria-expanded': `${open}`, disabled: !closeable, classes: [ fixedCss.titleButtonFixed, ...this.theme([css.titleButton]) ], + focus: this.shouldFocus, id: `${this._id}-title`, type: 'button', onclick: this._onTitleClick diff --git a/src/title-pane/tests/unit/TitlePane.ts b/src/title-pane/tests/unit/TitlePane.ts index 6f1df3f94e..74be8ea67a 100644 --- a/src/title-pane/tests/unit/TitlePane.ts +++ b/src/title-pane/tests/unit/TitlePane.ts @@ -69,6 +69,7 @@ const expected = function(options: {open?: boolean, closeable?: boolean, heading 'aria-expanded': `${open}`, classes: [ fixedCss.titleButtonFixed, css.titleButton ], disabled: !closeable, + focus: noop, id: '', type: 'button', onclick: noop From 1e707342e6dd46175fed5540637968deba466f02 Mon Sep 17 00:00:00 2001 From: smhigley Date: Wed, 10 Oct 2018 23:27:43 -0700 Subject: [PATCH 2/2] increase delay for focus tests --- src/combobox/tests/functional/ComboBox.ts | 4 ++-- src/select/tests/unit/Select.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/combobox/tests/functional/ComboBox.ts b/src/combobox/tests/functional/ComboBox.ts index a89f66ffdb..a6eef9be5e 100644 --- a/src/combobox/tests/functional/ComboBox.ts +++ b/src/combobox/tests/functional/ComboBox.ts @@ -126,7 +126,7 @@ registerSuite('ComboBox', { return getPage(this.remote) .findByCssSelector(`.${css.clear}`) .click() - .sleep(30) + .sleep(DELAY) .getActiveElement() .getTagName() .then(tag => { @@ -135,7 +135,7 @@ registerSuite('ComboBox', { .type(keys.TAB) .getActiveElement() .type(keys.ENTER) - .sleep(30) + .sleep(DELAY) .getActiveElement() .getTagName() .then(tag => { diff --git a/src/select/tests/unit/Select.ts b/src/select/tests/unit/Select.ts index a4133bf423..3a89148606 100644 --- a/src/select/tests/unit/Select.ts +++ b/src/select/tests/unit/Select.ts @@ -563,7 +563,7 @@ registerSuite('Select', { ...stubEvent, key: 'T' }); - h.expect(() => expected(expectedSingle(true, false, false, '', 1, false))); + h.expect(() => expected(expectedSingle(true, false, false, '', 1))); }, 'select option in menu based on input key'() { @@ -584,7 +584,7 @@ registerSuite('Select', { ...stubEvent, which: Keys.Enter }); - h.expect(() => expected(expectedSingle(true, false, true, '', 1, false))); + h.expect(() => expected(expectedSingle(true, false, true, '', 1))); }, 'select option in menu based on multiple input keys'() { @@ -609,7 +609,7 @@ registerSuite('Select', { ...stubEvent, which: Keys.Enter }); - h.expect(() => expected(expectedSingle(true, false, true, '', 2, false))); + h.expect(() => expected(expectedSingle(true, false, true, '', 2))); }, 'does not select disabled options based on input key'() { @@ -626,7 +626,7 @@ registerSuite('Select', { ...stubEvent, key: 'o' }); - h.expect(() => expected(expectedSingle(true, false, false, '', 0, false))); + h.expect(() => expected(expectedSingle(true, false, false, '', 0))); } } }