Skip to content

Commit

Permalink
Maintenance: Lint Rule: A11y with recommended settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Scharf committed Apr 23, 2024
1 parent 7c7cdba commit 818035e
Show file tree
Hide file tree
Showing 63 changed files with 263 additions and 198 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Expand Up @@ -21,6 +21,7 @@ module.exports = {
plugins: [
'@typescript-eslint',
'vue',
'vuejs-accessibility',
'prettier',
'sonarjs',
'security',
Expand All @@ -29,6 +30,7 @@ module.exports = {
extends: [
'airbnb-base',
'plugin:vue/vue3-recommended',
'plugin:vuejs-accessibility/recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
Expand Down
Expand Up @@ -84,6 +84,7 @@ export default {
v-model="filter"
v-bind="$attrs"
:placeholder="i18n.t(placeholder)"
:aria-label="$t('Search…')"
class="w-full min-w-16 text-black outline-none dark:text-white"
:class="{
'bg-blue-200 dark:bg-gray-700': !alternativeBackground,
Expand Down
Expand Up @@ -6,11 +6,17 @@ export interface Props {
max?: string
}
const props = defineProps<Props>()
defineProps<Props>()
</script>

<template>
<progress class="progress" :value="props.value" :max="props.max"></progress>
<progress
class="progress"
tabindex="0"
:aria-label="$t('Indicating progress')"
:value="value"
:max="max"
/>
</template>

<style scoped>
Expand Down
Expand Up @@ -347,7 +347,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
class="ms-auto text-blue-800 focus-visible:rounded-sm focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800 dark:text-blue-800"
prefix-icon="check-all"
role="button"
tabindex="1"
tabindex="0"
@click.stop="selectAll()"
@keypress.enter.prevent.stop="selectAll()"
@keypress.space.prevent.stop="selectAll()"
Expand Down
Expand Up @@ -61,7 +61,7 @@ const OptionIconComponent = props.optionIconComponent
:class="{
'pointer-events-none': option.disabled,
}"
:tabindex="option.disabled ? '-1' : '0'"
tabindex="0"
:aria-selected="selected"
:aria-disabled="option.disabled ? 'true' : undefined"
class="group flex h-9 cursor-pointer items-center gap-1.5 self-stretch px-2.5 text-sm text-black outline-none hover:bg-blue-600 focus:bg-blue-800 focus:text-white hover:focus:focus:bg-blue-800 dark:text-white dark:hover:bg-blue-900"
Expand Down
Expand Up @@ -408,7 +408,7 @@ useFormBlock(contextReactive, openSelectDropdown)
:aria-describedby="context.describedBy"
aria-autocomplete="none"
:data-multiple="context.multiple"
:tabindex="context.disabled ? '-1' : '0'"
tabindex="0"
v-bind="context.attrs"
@keydown.escape.prevent="closeDropdown()"
@keypress.enter.prevent="openSelectDropdown()"
Expand Down
Expand Up @@ -1024,7 +1024,7 @@ describe('Form - Field - AutoComplete - Accessibility', () => {
name: 'select all options',
})

expect(selectAllButton).toHaveAttribute('tabindex', '1')
expect(selectAllButton).toHaveAttribute('tabindex', '0')

const listbox = getByRole(menu, 'listbox')

Expand Down Expand Up @@ -1063,7 +1063,7 @@ describe('Form - Field - AutoComplete - Accessibility', () => {
expect(selectField).toHaveFocus()
})

it('prevents focusing of disabled field', async () => {
it('allows focusing of disabled field for a11y', async () => {
const wrapper = renderComponent(FormKit, {
...wrapperParameters,
props: {
Expand All @@ -1073,7 +1073,7 @@ describe('Form - Field - AutoComplete - Accessibility', () => {
},
})

expect(wrapper.getByLabelText('Select…')).toHaveAttribute('tabindex', '-1')
expect(wrapper.getByLabelText('Select…')).toHaveAttribute('tabindex', '0')
})

it('prevents opening of dropdown in disabled field', async () => {
Expand Down
Expand Up @@ -63,6 +63,7 @@ const dark = computed(() => theme.value === 'dark')

<template>
<div class="w-full">
<!-- eslint-disable vuejs-accessibility/aria-props -->
<VueDatePicker
v-model="localValue"
:uid="context.id"
Expand All @@ -86,7 +87,6 @@ const dark = computed(() => theme.value === 'dark')
:position="position"
:action-row="actionRow"
:config="config"
:input-class-name="context.classes.input"
:aria-labels="ariaLabels"
auto-apply
text-input
Expand Down
Expand Up @@ -38,30 +38,32 @@ const selectOption = (option: RadioListOption, event?: Event) => {
<output
:id="context.id"
class="flex flex-col items-start rounded-lg bg-blue-200 focus:outline focus:outline-1 focus:outline-offset-1 focus:outline-blue-800 hover:focus:outline-blue-800 dark:bg-gray-700"
role="listbox"
role="radiogroup"
:class="context.classes.input"
:name="context.node.name"
:aria-disabled="context.disabled"
:aria-describedby="context.describedBy"
:tabindex="context.disabled ? '-1' : '0'"
tabindex="0"
v-bind="context.attrs"
@focus="delegateFocus"
>
<!-- eslint-disable vuejs-accessibility/interactive-supports-focus -->
<div
v-for="option in context.options"
:key="`option-${option.value}`"
class="group inline-flex cursor-pointer gap-2.5 px-3 py-2.5"
role="option"
role="radio"
:aria-disabled="context.disabled"
:aria-checked="option.value == localValue"
:aria-label="option.label"
@click.stop="selectOption(option, $event)"
@keydown.enter.stop="selectOption(option, $event)"
>
<CommonIcon
:id="`radio_list_radio_${context.id}_${option.value}`"
size="small"
tabindex="0"
class="formkit-disabled:pointer-events-none formkit-invalid:outline-red-500 dark:hover:formkit-invalid:outline-red-500 formkit-errors:outline formkit-errors:outline-1 formkit-errors:-outline-offset-1 formkit-errors:outline-red-500 dark:hover:formkit-errors:outline-red-500 shrink-0 self-start rounded-full focus:outline focus:outline-1 focus:-outline-offset-1 focus:outline-blue-800 group-hover:outline group-hover:outline-1 group-hover:-outline-offset-1 group-hover:outline-blue-600 group-hover:focus:outline-blue-800 dark:group-hover:outline-blue-900 dark:group-hover:focus:outline-blue-800"
:tabindex="context.disabled ? '-1' : '0'"
:name="option.value == localValue ? 'radio-yes' : 'radio-no'"
@keydown.space.prevent="selectOption(option)"
/>
Expand Down
Expand Up @@ -61,7 +61,7 @@ describe('Form - Field - Radio List', () => {
it('renders given options', async () => {
const wrapper = await renderRadioListInput()

const selectOptions = wrapper.getAllByRole('option')
const selectOptions = wrapper.getAllByRole('radio')

expect(selectOptions).toHaveLength(testOptions.length)

Expand All @@ -75,7 +75,7 @@ describe('Form - Field - Radio List', () => {
options: testOptionsWithDescription,
})

const selectOptions = wrapper.getAllByRole('option')
const selectOptions = wrapper.getAllByRole('radio')

expect(selectOptions).toHaveLength(testOptionsWithDescription.length)

Expand Down Expand Up @@ -127,7 +127,7 @@ describe('Fields - Field Radio List - Input Checklist', () => {
const wrapper = await renderRadioListInput()

for await (const [i, item] of [testOptions[1], testOptions[2]].entries()) {
wrapper.events.click(wrapper.getByRole('option', { name: item.label }))
wrapper.events.click(wrapper.getByRole('radio', { name: item.label }))

await waitFor(() => {
expect(wrapper.emitted().inputRaw[i]).toBeTruthy()
Expand All @@ -144,13 +144,13 @@ describe('Fields - Field Radio List - Input Checklist', () => {
value: [testOptions[1].value],
})

const radio1 = wrapper.getByRole('option', { name: testOptions[0].label })
const radio1 = wrapper.getByRole('radio', { name: testOptions[0].label })
expect(radio1).toHaveAttribute('aria-checked', 'false')

const radio2 = wrapper.getByRole('option', { name: testOptions[1].label })
const radio2 = wrapper.getByRole('radio', { name: testOptions[1].label })
expect(radio2).toHaveAttribute('aria-checked', 'true')

const radio3 = wrapper.getByRole('option', { name: testOptions[2].label })
const radio3 = wrapper.getByRole('radio', { name: testOptions[2].label })
expect(radio3).toHaveAttribute('aria-checked', 'false')
})

Expand All @@ -162,7 +162,7 @@ describe('Fields - Field Radio List - Input Checklist', () => {
expect(view.getByLabelText('Radio list')).toBeDisabled()

for (const option of testOptions) {
expect(view.getByRole('option', { name: option.label })).toBeDisabled()
expect(view.getByRole('radio', { name: option.label })).toBeDisabled()
}
})

Expand Down
Expand Up @@ -170,6 +170,7 @@ setupMissingOrDisabledOptionHandling()
@select="selectOption"
@close="onCloseDropdown"
>
<!-- eslint-disable vuejs-accessibility/interactive-supports-focus-->
<output
:id="context.id"
ref="outputElement"
Expand All @@ -184,9 +185,7 @@ setupMissingOrDisabledOptionHandling()
:aria-disabled="context.disabled"
:data-multiple="context.multiple"
:aria-describedby="context.describedBy"
:tabindex="
context.disabled || (expanded && !context.noFiltering) ? '-1' : '0'
"
:tabindex="expanded && !context.noFiltering ? '-1' : '0'"
v-bind="context.attrs"
@keydown.escape.prevent="closeDropdown()"
@keypress.enter.prevent="openSelectDropdown()"
Expand Down
Expand Up @@ -1141,7 +1141,7 @@ describe('Form - Field - Select - Accessibility', () => {
name: 'select all options',
})

expect(selectAllButton).toHaveAttribute('tabindex', '1')
expect(selectAllButton).toHaveAttribute('tabindex', '0')

const listbox = getByRole(menu, 'listbox')

Expand Down Expand Up @@ -1180,17 +1180,18 @@ describe('Form - Field - Select - Accessibility', () => {
expect(selectField).toHaveFocus()
})

it('prevents focusing of disabled field', async () => {
it('allows focusing of disabled field for a11y', async () => {
const wrapper = renderComponent(FormKit, {
...wrapperParameters,
props: {
...commonProps,
type: 'select',
options: testOptions,
disabled: true,
},
})

expect(wrapper.getByLabelText('Select')).toHaveAttribute('tabindex', '-1')
expect(wrapper.getByLabelText('Select')).toHaveAttribute('tabindex', '0')
})

it('prevents opening of dropdown in disabled field', async () => {
Expand Down
Expand Up @@ -247,7 +247,7 @@ setupMissingOrDisabledOptionHandling()
role="combobox"
:name="context.node.name"
class="flex grow items-center gap-2.5 px-2.5 py-2 text-black focus:outline-none dark:text-white"
:tabindex="context.disabled ? '-1' : '0'"
tabindex="0"
:aria-labelledby="`label-${context.id}`"
:aria-disabled="context.disabled ? 'true' : undefined"
v-bind="context.attrs"
Expand Down
Expand Up @@ -441,7 +441,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
"
:aria-label="$t('Back to previous page')"
role="button"
tabindex="1"
tabindex="0"
@click.stop="goToPreviousPage(true)"
@keypress.enter.prevent.stop="goToPreviousPage()"
@keypress.space.prevent.stop="goToPreviousPage()"
Expand All @@ -453,7 +453,7 @@ const duration = VITE_TEST_MODE ? undefined : { enter: 300, leave: 200 }
class="ms-auto text-blue-800 focus-visible:rounded-sm focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-blue-800 dark:text-blue-800"
prefix-icon="check-all"
role="button"
tabindex="2"
tabindex="0"
@click.stop="selectAll(true)"
@keypress.enter.prevent.stop="selectAll()"
@keypress.space.prevent.stop="selectAll()"
Expand Down
Expand Up @@ -56,7 +56,7 @@ const goToNextPage = (option: FlatSelectOption, noFocus?: boolean) => {
'cursor-pointer hover:bg-blue-600 focus:bg-blue-800 focus:text-white dark:hover:bg-blue-900 dark:hover:focus:bg-blue-800':
!option.disabled,
}"
:tabindex="option.disabled ? '-1' : '0'"
tabindex="0"
:aria-selected="selected"
:aria-disabled="option.disabled ? 'true' : undefined"
class="group flex h-9 items-center gap-1.5 self-stretch px-2.5 text-sm text-black outline-none dark:text-white"
Expand Down
Expand Up @@ -1260,7 +1260,7 @@ describe('Form - Field - TreeSelect - Accessibility', () => {
name: 'select all options',
})

expect(selectAllButton).toHaveAttribute('tabindex', '2')
expect(selectAllButton).toHaveAttribute('tabindex', '0')

const listbox = getByRole(menu, 'listbox')

Expand All @@ -1282,23 +1282,21 @@ describe('Form - Field - TreeSelect - Accessibility', () => {

expect(
getByRole(menu, 'button', { name: 'Back to previous page' }),
).toHaveAttribute('tabindex', '1')
).toHaveAttribute('tabindex', '0')
})

it('prevents focusing of disabled field', async () => {
it('allows focusing of disabled field for a11y', async () => {
const wrapper = renderComponent(FormKit, {
...wrapperParameters,
props: {
...commonProps,
label: 'Select…',
type: 'treeselect',
options: testOptions,
disabled: true,
},
})

expect(wrapper.getByLabelText('Treeselect')).toHaveAttribute(
'tabindex',
'-1',
)
expect(wrapper.getByLabelText('Select…')).toHaveAttribute('tabindex', '0')
})

it('restores focus on close', async () => {
Expand Down
Expand Up @@ -69,6 +69,7 @@ watchEffect(() => {
ref="searchTextField"
v-model.trim="searchText"
:placeholder="$t('Apply filter…')"
:aria-label="$t('Navigation filter')"
class="w-0 bg-transparent text-sm text-black transition-[width] duration-200 focus:outline-none dark:text-white"
:class="{ 'w-full': filterFieldOpen }"
type="text"
Expand Down
Expand Up @@ -23,7 +23,6 @@ withDefaults(defineProps<Props>(), {
<header
class="group/heading flex cursor-default justify-between px-0 text-base font-normal leading-5 text-stone-200 active:text-stone-200 dark:text-neutral-500 dark:active:text-neutral-500"
:class="{ 'cursor-pointer': collapsible }"
@click="collapsible && $emit('toggle-collapsed', title)"
>
<slot name="title">
<h4 class="grow text-base ltr:mr-auto rtl:ml-auto">
Expand All @@ -37,6 +36,7 @@ withDefaults(defineProps<Props>(), {
group="heading"
class="mt-0.5 rtl:order-1"
orientation="vertical"
@click.stop="collapsible && $emit('toggle-collapsed', title)"
/>
</header>
</template>
Expand Down
Expand Up @@ -68,6 +68,8 @@ const hoverPoweredByLogo = ref(false)
open-in-new-tab
external
class="flex items-center gap-1 text-neutral-500"
@focus="hoverPoweredByLogo = true"
@blur="hoverPoweredByLogo = false"
@mouseover="hoverPoweredByLogo = true"
@mouseleave="hoverPoweredByLogo = false"
>
Expand Down

0 comments on commit 818035e

Please sign in to comment.