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

fix(VMenu/Selects): properly configure a11y #7385

Merged
merged 33 commits into from
Jun 18, 2019
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ade8386
chore(routable): remove invalid attribute
johnleider Jun 3, 2019
84f0bf8
refactor(activatable): pass aria attributes through scoped slot
johnleider Jun 3, 2019
61282b8
fix(VMenu): update for aria support
johnleider Jun 3, 2019
17101cb
chore: experimenting with interpretted aria attributes
johnleider Jun 3, 2019
2f9a46f
fix: update all inputs using v-menu for aria support
johnleider Jun 3, 2019
9ab5bbf
refactor: improve interaction with selects
johnleider Jun 3, 2019
6f84d8c
chore(VSelect): remove dev code
johnleider Jun 4, 2019
33b1f46
refactor: improve native feel of select inputs
johnleider Jun 4, 2019
c568e66
Merge branch 'next' into fix/menu-a11y
johnleider Jun 8, 2019
1996e89
fix(VSelect): update from feedback
johnleider Jun 8, 2019
324fe7f
test: fix unit tests from improvments
johnleider Jun 8, 2019
9568d7e
refactor(VSelect): change computeId logic and change default id
johnleider Jun 8, 2019
9c3906e
Merge branch 'next' into fix/menu-a11y
johnleider Jun 12, 2019
fe062f0
test(VListItem): improve a11y coverage
johnleider Jun 12, 2019
e0c6e31
style(VListeItem): fix spec lint
johnleider Jun 12, 2019
9eb4343
test(VMenu): expand a11y coverage
johnleider Jun 12, 2019
6c91ae6
test(VSelect): expand a11y coverage
johnleider Jun 12, 2019
bf005ac
test(VAutocomplete): expand a11y coverage
johnleider Jun 12, 2019
cc4b6a2
Update packages/vuetify/src/components/VAutocomplete/__tests__/VAutoc…
johnleider Jun 13, 2019
b642815
Update packages/vuetify/src/components/VAutocomplete/__tests__/VAutoc…
johnleider Jun 13, 2019
1e4efda
Update packages/vuetify/src/components/VList/__tests__/VListItem.spec.ts
johnleider Jun 13, 2019
18298f9
style(VAutocomplete): fix lint
johnleider Jun 13, 2019
4836dd6
Update packages/vuetify/src/components/VBtn/VBtn.ts
johnleider Jun 14, 2019
d2a5229
chore(VSelect): tweaks from review
johnleider Jun 14, 2019
4bb4344
Merge branch 'next' into fix/menu-a11y
johnleider Jun 14, 2019
ed22546
Merge branch 'next' into fix/menu-a11y
johnleider Jun 17, 2019
26417f3
fix(VBtn): only add disabled attr when tag is button
johnleider Jun 17, 2019
b2c7c0a
Merge branch 'next' into fix/menu-a11y
johnleider Jun 17, 2019
0db9ae8
refactor(VMenu): improve keyboard functionality
johnleider Jun 17, 2019
5f60b81
fix(VMenu): update tab functionality to match wai-aria
johnleider Jun 17, 2019
8a199e1
refactor(VSelect): avoid iterating menu items if no selections
johnleider Jun 17, 2019
7fc8526
refactor(VSelect): remove tab as a selection key for native behavior
johnleider Jun 17, 2019
4432081
Merge branch 'next' into fix/menu-a11y
dsseng Jun 18, 2019
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
32 changes: 19 additions & 13 deletions packages/vuetify/src/components/VAutocomplete/VAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export default VSelect.extend({

data () {
return {
attrsInput: null,
lazySearch: this.searchInput,
}
},
Expand Down Expand Up @@ -198,7 +197,11 @@ export default VSelect.extend({
},

methods: {
onFilteredItemsChanged (val: never[]) {
onFilteredItemsChanged (val: never[], oldVal: never[]) {
johnleider marked this conversation as resolved.
Show resolved Hide resolved
// TODO: How is the watcher triggered
// for duplicate items? no idea
if (val === oldVal) return

this.$nextTick(() => {
this.setMenuIndex(val.length > 0 && (val.length === 1 || this.autoSelectFirst) ? 0 : -1)
})
Expand Down Expand Up @@ -275,12 +278,17 @@ export default VSelect.extend({
input.data = input.data || {}
input.data.attrs = input.data.attrs || {}
input.data.domProps = input.data.domProps || {}

input.data.attrs.role = 'combobox'
input.data.domProps.value = this.internalSearch

return input
},
genInputSlot () {
const slot = VSelect.options.methods.genInputSlot.call(this)

slot.data!.attrs!.role = 'combobox'

return slot
},
genSelections () {
return this.hasSlot || this.multiple
? VSelect.options.methods.genSelections.call(this)
Expand All @@ -295,11 +303,6 @@ export default VSelect.extend({

this.activateMenu()
},
onEnterDown () {
// Avoid invoking this method
// will cause updateSelf to
// be called emptying search
},
onInput (e: Event) {
if (
this.selectedIndex > -1 ||
Expand All @@ -310,10 +313,7 @@ export default VSelect.extend({
const value = target.value

// If typing and menu is not currently active
if (target.value) {
this.activateMenu()
if (!this.isAnyValueAllowed) this.setMenuIndex(0)
}
if (target.value) this.activateMenu()

this.internalSearch = value
this.badInput = target.validity && target.validity.badInput
Expand All @@ -333,6 +333,12 @@ export default VSelect.extend({
VSelect.options.methods.onTabDown.call(this, e)
this.updateSelf()
},
onUpDown () {
// For autocomplete / combobox, cycling
// interfers with native up/down behavior
// instead activate the menu
this.activateMenu()
},
setSelectedItems () {
VSelect.options.methods.setSelectedItems.call(this)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe('VAutocomplete.ts', () => {

expect(wrapper.vm.$el.getAttribute('role')).toBeFalsy()

const input = wrapper.find('input')
const input = wrapper.find('.v-input__slot')
expect(input.element.getAttribute('role')).toBe('combobox')
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,16 +431,14 @@ describe('VAutocomplete.ts', () => {
})

it('should not replicate html select hotkeys in v-autocomplete', async () => {
// const wrapper = mountFunction()
const onKeyPress = jest.fn()
const wrapper = mountFunction({
propsData: {
items: ['aaa', 'foo', 'faa'],
},
methods: { onKeyPress },
})

const onKeyPress = jest.fn()
wrapper.setMethods({ onKeyPress })

const input = wrapper.find('input')
input.trigger('focus')
await wrapper.vm.$nextTick()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Components
import VAutocomplete from '../VAutocomplete'

// Utilities
import {
mount,
Wrapper,
johnleider marked this conversation as resolved.
Show resolved Hide resolved
MountOptions,
} from '@vue/test-utils'

describe('VAutocomplete.ts', () => {
type Instance = InstanceType<typeof VAutocomplete>
let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>

beforeEach(() => {
document.body.setAttribute('data-app', 'true')

mountFunction = (options = {}) => {
return mount(VAutocomplete, {
...options,
mocks: {
$vuetify: {
lang: {
t: (val: string) => val,
},
theme: {
dark: false,
},
},
},
})
}
})

it('should have the correct role', async () => {
const wrapper = mountFunction()

const inputSlot = wrapper.find('.v-input__slot')

expect(inputSlot.element.getAttribute('role')).toBe('combobox')
})
})
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VBtn/VBtn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export default baseMixins.extend<options>().extend({
const { tag, data } = this.generateRouteLink()

if (tag === 'button') data.attrs!.type = this.type

if (this.disabled) data.attrs!.disabled = true
johnleider marked this conversation as resolved.
Show resolved Hide resolved
data.attrs!.value = ['string', 'number'].includes(typeof this.value)
? this.value
: JSON.stringify(this.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Block
.v-simple-checkbox
align-self: center
line-height: normal
position: relative
user-select: none
cursor: pointer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default Vue.extend({
if (props.indeterminate) icon = props.indeterminateIcon
else if (props.value) icon = props.onIcon

children.push(h(VIcon, Colorable.options.methods.setTextColor(props.color, {
children.push(h(VIcon, Colorable.options.methods.setTextColor(props.value && props.color, {
props: {
disabled: props.disabled,
dark: props.dark,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ exports[`VColorPickerEdit.ts should render with disabled 1`] = `
A
</span>
</div>
<button disabled="disabled"
type="button"
<button type="button"
disabled="disabled"
class="v-btn v-btn--disabled v-btn--fab v-btn--flat v-btn--icon v-btn--round theme--light v-size--small"
>
<span class="v-btn__content">
Expand Down
9 changes: 6 additions & 3 deletions packages/vuetify/src/components/VCombobox/VCombobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export default VAutocomplete.extend({
},

methods: {
onFilteredItemsChanged: () => {},
onInternalSearchChanged (val: any) {
if (
val &&
Expand Down Expand Up @@ -94,13 +93,15 @@ export default VAutocomplete.extend({
onEnterDown (e: Event) {
e.preventDefault()

VSelect.options.methods.onEnterDown.call(this, e)

// If has menu index, let v-select-list handle
if (this.getMenuIndex() > -1) return

this.updateSelf()
},
onFilteredItemsChanged () {
// noop - should not auto select so user can enter custom
// words that may loosely match items in the list
},
onKeyDown (e: KeyboardEvent) {
const keyCode = e.keyCode

Expand All @@ -113,6 +114,8 @@ export default VAutocomplete.extend({
this.$refs.input.selectionStart === 0
) {
this.updateSelf()
} else if (keyCode === keyCodes.enter) {
this.onEnterDown(e)
}

// The ordering is important here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,24 @@ describe('VCombobox.ts', () => {

const input = wrapper.find('input')
const element = input.element as HTMLInputElement
const menu = wrapper.find('.v-menu')

input.trigger('focus')
element.value = 'b'
input.trigger('input')
menu.trigger('keydown.down')
// First down opens the menu
input.trigger('keydown.down')

// Give DOM time to update
// list tile classes
await wrapper.vm.$nextTick()

expect(wrapper.vm.isMenuActive).toBe(true)

// Second down moves the index
input.trigger('keydown.down')
input.trigger('keydown.tab')

expect(change).toHaveBeenCalledWith(['bar'])
expect(wrapper.vm.getMenuIndex()).toBe(0)
})

it('should add a tag on tab using the current searchValue', async () => {
Expand All @@ -134,15 +135,18 @@ describe('VCombobox.ts', () => {
})

const input = wrapper.find('input')
const element = input.element as HTMLInputElement

input.trigger('focus')
element.value = 'ba'
input.trigger('input')

wrapper.setProps({ searchInput: 'ba' })
input.trigger('keydown.tab')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['ba'])

wrapper.setProps({ searchInput: 'it' })
element.value = 'it'
input.trigger('input')
input.trigger('keydown.tab')
await wrapper.vm.$nextTick()
expect(change).toHaveBeenCalledWith(['ba', 'it'])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,13 @@ describe('VCombobox.ts', () => {
})

// https://github.com/vuetifyjs/vuetify/issues/5008
it.skip('should select item if menu index is greater than -1', async () => {
it('should select item if menu index is greater than -1', async () => {
const selectItem = jest.fn()
const wrapper = mountFunction({
propsData: {
items: ['foo'],
},
methods: { selectItem },
})

const input = wrapper.find('input')
Expand All @@ -263,6 +265,6 @@ describe('VCombobox.ts', () => {

input.trigger('keydown.enter')

expect(wrapper.vm.internalValue).toBe('foo')
expect(selectItem).toHaveBeenCalledWith('foo')
})
})
Loading