Skip to content

Commit

Permalink
feat(a11y): improve refocus after popup close with ESC; improve compo…
Browse files Browse the repository at this point in the history
…nents with popups (#14784)

* fix(QFab): fix selector specificity when open with rtlcss enabled

* feat(QMenu,QDialog): refocus a kbd focusable element when closing using ESC key

* fix(QList): remove default list role because it is used now all over as role menu

* feat(QBtnDropdown,QExpansionItem,QFab,QSelect): improve a11y related to popups

- QSelect: add a11y readable value if not using multiple and if showing value in input for useInput
  • Loading branch information
pdanpdan committed Nov 14, 2022
1 parent d854c92 commit 4c407da
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 37 deletions.
4 changes: 2 additions & 2 deletions ui/dev/src/pages/components/fab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@

<q-fab
class="fixed-bottom-right"
style="right: 18px; bottom: 86px;"
style="inset-inline-end: 18px; bottom: 86px;"
icon="fas fa-address-book"
direction="up"
color="primary"
Expand Down Expand Up @@ -123,7 +123,7 @@
direction="up"
class="fixed-bottom-right"
:icon="mdiMenu"
style="right: 18px; bottom: 18px;"
style="inset-inline-end: 18px; bottom: 18px;"
>
<template v-slot:tooltip>
<q-tooltip ref="tooltip0" anchor="center left" self="center right" :offset="[20, 0]">
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/btn-dropdown/QBtnDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export default createComponent({
'aria-expanded': showing.value === true ? 'true' : 'false',
'aria-haspopup': 'true',
'aria-controls': targetUid,
'aria-owns': targetUid,
'aria-label': props.toggleAriaLabel || proxy.$q.lang.label[ showing.value === true ? 'collapse' : 'expand' ](props.label)
}

Expand Down
11 changes: 7 additions & 4 deletions ui/src/components/dialog/QDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,10 @@ export default createComponent({
hidePortal()

if (refocusTarget !== null) {
refocusTarget.focus()
((evt && evt.type.indexOf('key') === 0
? refocusTarget.closest('[tabindex]:not([tabindex^="-"])')
: void 0
) || refocusTarget).focus()
refocusTarget = null
}

Expand Down Expand Up @@ -248,9 +251,9 @@ export default createComponent({
})
}

function shake (refocusTarget) {
if (refocusTarget && typeof refocusTarget.focus === 'function') {
refocusTarget.focus({ preventScroll: true })
function shake (focusTarget) {
if (focusTarget && typeof focusTarget.focus === 'function') {
focusTarget.focus({ preventScroll: true })
}
else {
focus()
Expand Down
1 change: 0 additions & 1 deletion ui/src/components/expansion-item/QExpansionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ export default createComponent({
return {
role: 'button',
'aria-expanded': showing.value === true ? 'true' : 'false',
'aria-owns': targetUid,
'aria-controls': targetUid,
'aria-label': toggleAriaLabel
}
Expand Down
9 changes: 3 additions & 6 deletions ui/src/components/fab/QFab.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,11 @@ export default createComponent({

const actionAttrs = computed(() => {
const attrs = {
id: targetUid
id: targetUid,
role: 'menu'
}

if (showing.value === true) {
attrs.role = 'menu'
}
else {
if (showing.value !== true) {
attrs[ 'aria-hidden' ] = 'true'
}

Expand Down Expand Up @@ -152,7 +150,6 @@ export default createComponent({
'aria-expanded': showing.value === true ? 'true' : 'false',
'aria-haspopup': 'true',
'aria-controls': targetUid,
'aria-owns': targetUid,
onClick: toggle
}, getTriggerContent),

Expand Down
3 changes: 2 additions & 1 deletion ui/src/components/fab/QFab.sass
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@

&--opened
opacity: 1
transform: scale(1) translate(0, 0)
// needed when rtlcss is enabled
transform: scale(1) translate(0.1px, 0)
pointer-events: all

&--align-left
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/item/QList.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ export default createComponent({
+ (props.padding === true ? ' q-list--padding' : '')
)

return () => h(props.tag, { class: classes.value, role: 'list' }, hSlot(slots.default))
return () => h(props.tag, { class: classes.value }, hSlot(slots.default))
}
})
5 changes: 4 additions & 1 deletion ui/src/components/menu/QMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,10 @@ export default createComponent({
|| evt.qClickOutside !== true
)
) {
refocusTarget.focus()
((evt && evt.type.indexOf('key') === 0
? refocusTarget.closest('[tabindex]:not([tabindex^="-"])')
: void 0
) || refocusTarget).focus()
refocusTarget = null
}

Expand Down
43 changes: 23 additions & 20 deletions ui/src/components/select/QSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ export default createComponent({
.join(', ')
)

const ariaCurrentValue = computed(() => (props.displayValue !== void 0
? props.displayValue
: selectedString.value
))

const needsHtmlFn = computed(() => (
props.optionsHtml === true
? () => true
Expand All @@ -268,22 +273,15 @@ export default createComponent({

const tabindex = computed(() => (state.focused.value === true ? props.tabindex : -1))

const comboboxAttrs = computed(() => ({
tabindex: props.tabindex,
role: 'combobox',
'aria-label': props.label,
'aria-readonly': props.readonly === true ? 'true' : 'false',
'aria-autocomplete': props.useInput === true ? 'list' : 'none',
'aria-expanded': menu.value === true ? 'true' : 'false',
'aria-owns': `${ state.targetUid.value }_lb`,
'aria-controls': `${ state.targetUid.value }_lb`
}))

const listboxAttrs = computed(() => {
const comboboxAttrs = computed(() => {
const attrs = {
id: `${ state.targetUid.value }_lb`,
role: 'listbox',
'aria-multiselectable': props.multiple === true ? 'true' : 'false'
tabindex: props.tabindex,
role: 'combobox',
'aria-label': props.label,
'aria-readonly': props.readonly === true ? 'true' : 'false',
'aria-autocomplete': props.useInput === true ? 'list' : 'none',
'aria-expanded': menu.value === true ? 'true' : 'false',
'aria-controls': `${ state.targetUid.value }_lb`
}

if (optionIndex.value >= 0) {
Expand All @@ -293,6 +291,12 @@ export default createComponent({
return attrs
})

const listboxAttrs = computed(() => ({
id: `${ state.targetUid.value }_lb`,
role: 'listbox',
'aria-multiselectable': props.multiple === true ? 'true' : 'false'
}))

const selectedScope = computed(() => {
return innerValue.value.map((opt, i) => ({
index: i,
Expand Down Expand Up @@ -935,9 +939,7 @@ export default createComponent({

return [
h('span', {
[ valueAsHtml.value === true ? 'innerHTML' : 'textContent' ]: props.displayValue !== void 0
? props.displayValue
: selectedString.value
[ valueAsHtml.value === true ? 'innerHTML' : 'textContent' ]: ariaCurrentValue.value
})
]
}
Expand Down Expand Up @@ -992,7 +994,7 @@ export default createComponent({
id: isTarget === true ? state.targetUid.value : void 0,
maxlength: props.maxlength,
autocomplete: props.autocomplete,
'data-autofocus': (fromDialog !== true && props.autofocus === true) || void 0,
'data-autofocus': fromDialog === true || props.autofocus === true || void 0,
disabled: props.disable === true,
readonly: props.readonly === true,
...inputControlEvents.value
Expand Down Expand Up @@ -1492,8 +1494,9 @@ export default createComponent({
key: 'd_t',
class: 'q-select__focus-target',
id: isTarget === true ? state.targetUid.value : void 0,
value: ariaCurrentValue.value,
readonly: true,
'data-autofocus': (fromDialog !== true && props.autofocus === true) || void 0,
'data-autofocus': fromDialog === true || props.autofocus === true || void 0,
...attrs,
onKeydown: onTargetKeydown,
onKeyup: onTargetKeyup,
Expand Down

0 comments on commit 4c407da

Please sign in to comment.