Skip to content

Commit

Permalink
feat: add ARIA labels to multiselect components for improved accessib…
Browse files Browse the repository at this point in the history
…ility (#1820)
  • Loading branch information
akki-jat authored Jan 16, 2025
1 parent f70ef36 commit f86e2e1
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 95 deletions.
4 changes: 3 additions & 1 deletion documentation/src/components/MultiselectExample.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<template>
<div>
<multiselect :options="badges" v-model="value"
<multiselect :options="badges"
v-model="value"
label="name"
id="badges"
track-by="name"
placeholder="Pick badges"
:multiple="true"
Expand Down
6 changes: 3 additions & 3 deletions documentation/src/components/NavMenu.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<ul class="list" :class="{ 'list--sticky': isNavSticky }">
<multiselect class="list__multiselect" :options="versions" :model-value="version" :allow-empty="false"
:searchable="false"
id="version" :searchable="false"
@update:model-value="pickVersion" select-label="Go to" deselect-label=""
selected-label=""></multiselect>
selected-label="" aria-label="pick vue-multiselect version"></multiselect>
<li class="list__heading">Setup</li>
<li class="list__element"><a href="#sub-getting-started" class="link list__link" :class="{ 'list__link--active': currentPosition === 'sub-getting-started' }">Getting Started</a></li>
<li class="list__element"><a href="#sub-migration-guide" class="link list__link" :class="{ 'list__link--active': currentPosition === 'sub-migration-guide' }">Migration Guide</a></li>
Expand All @@ -30,7 +30,7 @@
<li class="list__element"><a target="_BLANK" href="https://vuelidate.netlify.com"
class="link list__link">Vuelidate<img
src="https://img.shields.io/github/stars/vuelidate/vuelidate.svg?style=social&amp;label=Stars"
class="list__img"><span class="list__desc">Simple model-based validation plugin for Vue.js</span></a></li>
class="list__img" alt="vuelidate library"><span class="list__desc">Simple model-based validation plugin for Vue.js</span></a></li>
</ul>
</template>
<script>
Expand Down
4 changes: 2 additions & 2 deletions documentation/src/components/_start.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<section class="start" :style="gradient">
<div class="center-vertically">
<h1 class="typo__h1"><img class="logo" src="/assets/vue-logo.png"/>Vue-multiselect<small class="version">{{version}}</small></h1>
<h1 class="typo__h1"><img class="logo" src="/assets/vue-logo.png" alt="vuejs logo"/>Vue-multiselect<small class="version">{{version}}</small></h1>
<h3 class="typo__h3">The most complete selecting solution for <a class="typo__link" href="http://vuejs.org" target="_BLANK">Vue.js</a></h3>
<div class="badges"><img src="https://img.shields.io/github/stars/shentao/vue-multiselect.svg?label=Stars" /><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License" data-canonical-src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" /><img src="https://img.shields.io/npm/dm/vue-multiselect.svg" alt="npm" data-canonical-src="https://img.shields.io/npm/dm/vue-multiselect.svg" style="max-width:100%;" /><img src="https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat" alt="No Dependencies" data-canonical-src="https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat" style="max-width:100%;" /></div>
<div class="badges"><img src="https://img.shields.io/github/stars/shentao/vue-multiselect.svg?label=Stars" alt="vue-multiselect library github stars" /><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License" data-canonical-src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" /><img src="https://img.shields.io/npm/dm/vue-multiselect.svg" alt="npm" data-canonical-src="https://img.shields.io/npm/dm/vue-multiselect.svg" style="max-width:100%;" /><img src="https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat" alt="No Dependencies" data-canonical-src="https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat" style="max-width:100%;" /></div>
<div class="grid__row grid__row--centered">
<div class="grid__column grid__unit--md-6">
<div class="multiselect-example__container">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div>
<label class="typo__label">Open console to see logs.</label>
<multiselect placeholder="Pick action" :options="actions" :searchable="false" :reset-after="true" @select="dispatchAction"></multiselect>
<multiselect id="action-dispatcher" placeholder="Pick action" :options="actions" :searchable="false" :reset-after="true" @select="dispatchAction" aria-label="pick a action"></multiselect>
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div :class="{ 'invalid': isInvalid }">
<label class="typo__label">Customized multiselect</label>
<multiselect placeholder="Pick at least one" select-label="Enter doesn’t work here!" :model-value="value" :options="options" :multiple="true" :searchable="true" :allow-empty="false" :prevent-autofocus="true" :hide-selected="true" :max-height="150" :max="3" :disabled="isDisabled" :block-keys="['Tab', 'Enter']" @update:modelValue="onChange" @close="onTouch" @select="onSelect"></multiselect>
<multiselect id="custom-configuration" placeholder="Pick at least one" select-label="Enter doesn’t work here!" :model-value="value" :options="options" :multiple="true" :searchable="true" :allow-empty="false" :prevent-autofocus="true" :hide-selected="true" :max-height="150" :max="3" :disabled="isDisabled" :block-keys="['Tab', 'Enter']" @update:modelValue="onChange" @close="onTouch" @select="onSelect"></multiselect>
<label class="typo__label form__label" v-show="isInvalid">Must have at least one value</label>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div><label class="typo__label">Custom option template</label>
<multiselect v-model="value" placeholder="Fav No Man’s Sky path" label="title" track-by="title" :options="options"
<multiselect id="custom-options" v-model="value" placeholder="Fav No Man’s Sky path" label="title" track-by="title" :options="options"
:option-height="104" :custom-label="customLabel" :show-labels="false">
<template #singleLabel="props"><img class="option__image" :src="props.option.img"
alt="No Man’s Sky"/><span class="option__desc"><span
Expand Down
2 changes: 1 addition & 1 deletion documentation/src/components/example-code/MultiSelect.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div><label class="typo__label">Simple select / dropdown</label>
<multiselect v-model="value" :options="options" :multiple="true" :close-on-select="false" :clear-on-select="false"
<multiselect id="multiselect" v-model="value" :options="options" :multiple="true" :close-on-select="false" :clear-on-select="false"
:preserve-search="true" placeholder="Pick some" label="name" track-by="name" :preselect-first="true">
<template #selection="{ values, search, isOpen }">
<span class="multiselect__single"
Expand Down
2 changes: 1 addition & 1 deletion documentation/src/components/example-code/OptionGroups.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div><label class="typo__label">Groups</label>
<multiselect v-model="value" :options="options" :multiple="true" group-values="libs" group-label="language"
<multiselect id="option-groups" v-model="value" :options="options" :multiple="true" group-values="libs" group-label="language"
:group-select="true" placeholder="Type to search" track-by="name" label="name"><template v-slot:noResult>Oops! No elements found. Consider changing the search query.</template>
</multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<button @click="close" class="button button-small">Close</button>
<pre>Multiselect Open: {{ isOpen }}</pre>
<label class="typo__label">Controlling multiselect programmatically</label>
<multiselect ref="multiselect" placeholder="Pick at least one" :value="value" :options="options" :multiple="true"
<multiselect id="programmatic-control" ref="multiselect" placeholder="Pick at least one" :value="value" :options="options" :multiple="true"
:searchable="true" :allow-empty="false" :hide-selected="true" :max-height="150" :max="3"
@open="isOpen = true" @close="isOpen = false"></multiselect>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div><label class="typo__label">Single select / dropdown</label>
<multiselect v-model="value" deselect-label="Can't remove this value" track-by="name" label="name"
placeholder="Select one" :options="options" :searchable="false" :allow-empty="false">
<multiselect id="single-select-object" v-model="value" deselect-label="Can't remove this value" track-by="name" label="name"
placeholder="Select one" :options="options" :searchable="false" :allow-empty="false"
aria-label="pick a value">
<template v-slot:singleLabel="{ option }"><strong>{{ option.name }}</strong> is written in <strong>
{{ option.language }}</strong></template>
</multiselect>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div><label class="typo__label">Single select</label>
<multiselect v-model="value" :options="options" :searchable="false" :close-on-select="false" :show-labels="false"
placeholder="Pick a value"></multiselect>
placeholder="Pick a value" aria-label="pick a value"></multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div><label class="typo__label">Select with search</label>
<multiselect v-model="value" :options="options" :custom-label="nameWithLang" placeholder="Select one" label="name"
track-by="name"></multiselect>
<multiselect id="single-select-search" v-model="value" :options="options" :custom-label="nameWithLang" placeholder="Select one" label="name"
track-by="name" aria-label="pick a value"></multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>
</template>
Expand Down
2 changes: 1 addition & 1 deletion documentation/src/components/example-code/Tagging.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div><label class="typo__label">Tagging</label>
<multiselect v-model="value" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name"
<multiselect id="tagging" v-model="value" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name"
track-by="code" :options="options" :multiple="true" :taggable="true" @tag="addTag"></multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>
Expand Down
2 changes: 1 addition & 1 deletion documentation/src/components/example-code/VuexSupport.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div><label class="typo__label">Vuex example.</label>
<multiselect placeholder="Pick action" :modelValue="value" :options="options" :searchable="false" @update:modelValue="updateValueAction"></multiselect>
<multiselect id="vuex-support" placeholder="Pick action" :modelValue="value" :options="options" :searchable="false" @update:modelValue="updateValueAction" aria-label="pick a action"></multiselect>
</div>
</template>

Expand Down
163 changes: 88 additions & 75 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src/Multiselect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
@keyup.esc="deactivate()"
class="multiselect"
role="combobox"
:aria-owns="'listbox-'+id">
:aria-expanded="isOpen"
:aria-owns="'listbox-'+id"
:aria-activedescendant="isOpen && pointer !== null ? id + '-' + pointer : null">
<slot name="caret" :toggle="toggle">
<div @mousedown.prevent.stop="toggle()" class="multiselect__select"></div>
</slot>
Expand Down Expand Up @@ -59,6 +61,7 @@
:value="search"
:disabled="disabled"
:tabindex="tabindex"
:aria-label="name + '-searchbox'"
@input="updateSearch($event.target.value)"
@focus.prevent="activate()"
@blur.prevent="deactivate()"
Expand Down

0 comments on commit f86e2e1

Please sign in to comment.