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(NcActions): correct dialog a11y attrs place #5381

Merged
merged 2 commits into from
Mar 8, 2024
Merged
Changes from all commits
Commits
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
63 changes: 49 additions & 14 deletions src/components/NcActions/NcActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1126,11 +1126,18 @@
'click',
],

setup() {
const randomId = `menu-${GenRandomId()}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! Does not need to be reactive!

return {
randomId,
triggerRandomId: `trigger-${randomId}`,
}
},

data() {
return {
opened: this.open,
focusIndex: 0,
randomId: `menu-${GenRandomId()}`,
/**
* @type {'menu'|'navigation'|'dialog'|'tooltip'|'unknown'}
*/
Expand All @@ -1155,6 +1162,10 @@
/**
* Accessibility notes:
*
* "aria-haspopup" and "aria-expanded" are managed by NcPopover via `popupRole`
*
* "aria-controls" should only present together with a valid aria-haspopup
*
* There is no valid popup role for navigation and tooltip in `aria-haspopup`.
* aria-haspopup="true" is equivalent to aria-haspopup="menu".
* They must not be treated as menus.
Expand All @@ -1164,31 +1175,61 @@
* Or the menu is an expanded list of UI elements.
*
* Navigation type is just an "expanded" block, similar to native <details> element.
*
* Arrow and Tab navigation should not be used together:
* - Arrow navigation is used to navigate between items of UI element
* - Tab navigation is used to navigate between UI elements on the page
* - Menu is either an atomic UI element of just an expanded block of elements
*/
const configs = {
menu: {
popupRole: 'menu',
withArrowNavigation: true,
withTabNavigation: false,
withFocusTrap: false,
triggerA11yAttr: {
'aria-controls': this.opened ? this.randomId : null,
},
popoverContainerA11yAttrs: {},
popoverUlA11yAttrs: {
id: this.randomId,
role: 'menu',
},
},
navigation: {
popupRole: undefined,
withArrowNavigation: false,
withTabNavigation: true,
withFocusTrap: false,
triggerA11yAttr: {},
popoverContainerA11yAttrs: {},
popoverUlA11yAttrs: {},
},
dialog: {
popupRole: 'dialog',
withArrowNavigation: false,
withTabNavigation: true,
withFocusTrap: true,
triggerA11yAttr: {
'aria-controls': this.opened ? this.randomId : null,
},
popoverContainerA11yAttrs: {
id: this.randomId,
role: 'dialog',
// Dialog must have a label
'aria-labelledby': this.triggerRandomId,
'aria-modal': 'true',
},
popoverUlA11yAttrs: {},
},
tooltip: {
popupRole: undefined,
withArrowNavigation: false,
withTabNavigation: false,
withFocusTrap: false,
triggerA11yAttr: {},
popoverContainerA11yAttrs: {},
popoverUlA11yAttrs: {},
},
// Due to Vue limitations, we sometimes cannot determine the true type
// As a fallback use both arrow navigation and focus trap
Expand All @@ -1198,14 +1239,13 @@
withArrowNavigation: true,
withTabNavigation: false,
withFocusTrap: true,
triggerA11yAttr: {},
popoverContainerA11yAttrs: {},
popoverUlA11yAttrs: {},
},
}
return configs[this.actionsMenuSemanticType]
},

withFocusTrap() {
return this.config.withFocusTrap
},
},

watch: {
Expand Down Expand Up @@ -1353,7 +1393,7 @@
return this.$refs.menu.querySelector('li.active')
},
/**
* @return {NodeListOf<HTMLElement>}

Check warning on line 1396 in src/components/NcActions/NcActions.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The type 'NodeListOf' is undefined
*/
getFocusableMenuItemElements() {
return this.$refs.menu.querySelectorAll(focusableSelector)
Expand Down Expand Up @@ -1725,7 +1765,6 @@
},
})
)
const triggerRandomId = `${this.randomId}-trigger`
return h('NcPopover',
{
ref: 'popover',
Expand Down Expand Up @@ -1769,10 +1808,9 @@
slot: 'trigger',
ref: 'menuButton',
attrs: {
id: triggerRandomId,
id: this.triggerRandomId,
'aria-label': this.menuName ? null : this.ariaLabel,
// 'aria-controls' should only present together with a valid aria-haspopup
'aria-controls': this.opened && this.config.popupRole ? this.randomId : null,
...this.config.triggerA11yAttr,
},
on: {
focus: this.onFocus,
Expand All @@ -1790,6 +1828,7 @@
},
attrs: {
tabindex: '-1',
...this.config.popoverContainerA11yAttrs,
},
on: {
keydown: this.onKeydown,
Expand All @@ -1799,12 +1838,8 @@
}, [
h('ul', {
attrs: {
id: this.randomId,
tabindex: '-1',
role: this.config.popupRole,
// Dialog must have a label
'aria-labelledby': this.actionsMenuSemanticType === 'dialog' ? triggerRandomId : undefined,
'aria-modal': this.actionsMenuSemanticType === 'dialog' ? 'true' : undefined,
...this.config.popoverUlA11yAttrs,
},
}, [
actions,
Expand Down
Loading