diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-30 16:42:28 +0100 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2023-10-31 21:56:13 +0100 |
commit | 716164c2850f835e236ad648f9774d5f6af5df07 (patch) | |
tree | b4d892280fa98ff1d343002e50e86dc95f220918 /apps/theming | |
parent | b4e707059def28bf4544ae43272bf62d5662961b (diff) | |
download | nextcloud-server-716164c2850f835e236ad648f9774d5f6af5df07.tar.gz nextcloud-server-716164c2850f835e236ad648f9774d5f6af5df07.zip |
fix(theming): Add accessible information for screen readers to the app order selector
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'apps/theming')
-rw-r--r-- | apps/theming/src/components/AppOrderSelector.vue | 71 | ||||
-rw-r--r-- | apps/theming/src/components/AppOrderSelectorElement.vue | 22 |
2 files changed, 80 insertions, 13 deletions
diff --git a/apps/theming/src/components/AppOrderSelector.vue b/apps/theming/src/components/AppOrderSelector.vue index 392519de25e..d8238ea2282 100644 --- a/apps/theming/src/components/AppOrderSelector.vue +++ b/apps/theming/src/components/AppOrderSelector.vue @@ -1,21 +1,34 @@ <template> - <ol ref="listElement" data-cy-app-order class="order-selector"> - <AppOrderSelectorElement v-for="app,index in appList" - :key="`${app.id}${renderCount}`" - ref="selectorElements" - :app="app" - :is-first="index === 0 || !!appList[index - 1].default" - :is-last="index === value.length - 1" - v-on="app.default ? {} : { - 'move:up': () => moveUp(index), - 'move:down': () => moveDown(index), - }" /> - </ol> + <Fragment> + <div :id="statusInfoId" + aria-live="polite" + class="hidden-visually" + role="status"> + {{ statusInfo }} + </div> + <ol ref="listElement" data-cy-app-order class="order-selector"> + <AppOrderSelectorElement v-for="app,index in appList" + :key="`${app.id}${renderCount}`" + ref="selectorElements" + :app="app" + :aria-details="ariaDetails" + :aria-describedby="statusInfoId" + :is-first="index === 0 || !!appList[index - 1].default" + :is-last="index === value.length - 1" + v-on="app.default ? {} : { + 'move:up': () => moveUp(index), + 'move:down': () => moveDown(index), + 'update:focus': () => updateStatusInfo(index), + }" /> + </ol> + </Fragment> </template> <script lang="ts"> +import { translate as t } from '@nextcloud/l10n' import { useSortable } from '@vueuse/integrations/useSortable' import { PropType, computed, defineComponent, onUpdated, ref } from 'vue' +import { Fragment } from 'vue-frag' import AppOrderSelectorElement from './AppOrderSelectorElement.vue' @@ -32,9 +45,17 @@ export default defineComponent({ name: 'AppOrderSelector', components: { AppOrderSelectorElement, + Fragment, }, props: { /** + * Details like status information that need to be forwarded to the interactive elements + */ + ariaDetails: { + type: String, + default: null, + }, + /** * List of apps to reorder */ value: { @@ -125,6 +146,28 @@ export default defineComponent({ emit('update:value', [...before, props.value[index], ...after]) } + /** + * Additional status information to show to screen reader users for accessibility + */ + const statusInfo = ref('') + + /** + * ID to be used on the status info element + */ + const statusInfoId = `sorting-status-info-${(Math.random() + 1).toString(36).substring(7)}` + + /** + * Update the status information for the currently selected app + * @param index Index of the app that is currently selected + */ + const updateStatusInfo = (index: number) => { + statusInfo.value = t('theming', 'Current selected app: {app}, position {position} of {total}', { + app: props.value[index].label, + position: index + 1, + total: props.value.length, + }) + } + return { appList, listElement, @@ -132,6 +175,10 @@ export default defineComponent({ moveDown, moveUp, + statusInfoId, + statusInfo, + updateStatusInfo, + renderCount, selectorElements, } diff --git a/apps/theming/src/components/AppOrderSelectorElement.vue b/apps/theming/src/components/AppOrderSelectorElement.vue index 4c7266eb798..23e7b0b07c9 100644 --- a/apps/theming/src/components/AppOrderSelectorElement.vue +++ b/apps/theming/src/components/AppOrderSelectorElement.vue @@ -3,7 +3,8 @@ :class="{ 'order-selector-element': true, 'order-selector-element--disabled': app.default - }"> + }" + @focusin="$emit('update:focus')"> <svg width="20" height="20" viewBox="0 0 20 20" @@ -25,6 +26,8 @@ <NcButton v-show="!isFirst && !app.default" ref="buttonUp" :aria-label="t('settings', 'Move up')" + :aria-describedby="ariaDescribedby" + :aria-details="ariaDetails" data-cy-app-order-button="up" type="tertiary-no-background" @click="moveUp"> @@ -36,6 +39,8 @@ <NcButton v-show="!isLast && !app.default" ref="buttonDown" :aria-label="t('settings', 'Move down')" + :aria-describedby="ariaDescribedby" + :aria-details="ariaDetails" data-cy-app-order-button="down" type="tertiary-no-background" @click="moveDown"> @@ -73,6 +78,17 @@ export default defineComponent({ NcButton, }, props: { + /** + * Needs to be forwarded to the buttons (as interactive elements) + */ + ariaDescribedby: { + type: String, + default: null, + }, + ariaDetails: { + type: String, + default: null, + }, app: { type: Object as PropType<IApp>, required: true, @@ -89,6 +105,10 @@ export default defineComponent({ emits: { 'move:up': () => true, 'move:down': () => true, + /** + * We need this as Sortable.js removes all native focus event listeners + */ + 'update:focus': () => true, }, setup(props, { emit }) { const buttonUp = ref() |