Accessibility for Slider

pull/2721/head
Tuğçe Küçükoğlu 2022-06-27 16:20:17 +03:00
parent a0e4b8d886
commit 5b62a009cd
4 changed files with 124 additions and 29 deletions

View File

@ -41,11 +41,23 @@ const SliderProps = [
default: "false", default: "false",
description: "When present, it specifies that the component should be disabled." description: "When present, it specifies that the component should be disabled."
}, },
{
name: "tabindex",
type: "number",
default: "null",
description: "Index of the element in tabbing order."
},
{ {
name: "ariaLabelledBy", name: "ariaLabelledBy",
type: "string", type: "string",
default: "null", default: "null",
description: "Establishes relationships between the component and label(s) where its value should be one or more element IDs." description: "Establishes relationships between the component and label(s) where its value should be one or more element IDs."
},
{
name: "ariaLabel",
type: "string",
default: "null",
description: "Used to define a string that labels the element."
} }
]; ];

View File

@ -48,10 +48,18 @@ export interface SliderProps {
* When present, it specifies that the component should be disabled. * When present, it specifies that the component should be disabled.
*/ */
disabled?: boolean | undefined; disabled?: boolean | undefined;
/**
* Index of the element in tabbing order.
*/
tabindex?: number | undefined;
/** /**
* Establishes relationships between the component and label(s) where its value should be one or more element IDs. * Establishes relationships between the component and label(s) where its value should be one or more element IDs.
*/ */
ariaLabelledBy?: string | undefined; ariaLabelledBy?: string | undefined;
/**
* Used to define a string that labels the element.
*/
ariaLabel?: string | undefined
} }
export interface SliderSlots { export interface SliderSlots {

View File

@ -1,12 +1,12 @@
<template> <template>
<div :class="containerClass" @click="onBarClick"> <div :class="containerClass" @click="onBarClick">
<span class="p-slider-range" :style="rangeStyle"></span> <span class="p-slider-range" :style="rangeStyle"></span>
<span v-if="!range" class="p-slider-handle" :style="handleStyle" @touchstart="onDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event)" @keydown="onKeyDown($event)" tabindex="0" <span v-if="!range" class="p-slider-handle" :style="handleStyle" @touchstart="onDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event)" @keydown="onKeyDown($event)" :tabindex="tabindex"
role="slider" :aria-valuemin="min" :aria-valuenow="modelValue" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy"></span> role="slider" :aria-valuemin="min" :aria-valuenow="modelValue" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy" :aria-label="ariaLabel" :aria-orientation="orientation"></span>
<span v-if="range" class="p-slider-handle" :style="rangeStartHandleStyle" @touchstart="onDragStart($event, 0)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event, 0)" @keydown="onKeyDown($event)" tabindex="0" <span v-if="range" class="p-slider-handle" :style="rangeStartHandleStyle" @touchstart="onDragStart($event, 0)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event, 0)" @keydown="onKeyDown($event)" :tabindex="tabindex"
role="slider" :aria-valuemin="min" :aria-valuenow="modelValue ? modelValue[0] : null" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy"></span> role="slider" :aria-valuemin="min" :aria-valuenow="modelValue ? modelValue[0] : null" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy" :aria-label="ariaLabel" :aria-orientation="orientation"></span>
<span v-if="range" class="p-slider-handle" :style="rangeEndHandleStyle" @touchstart="onDragStart($event, 1)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event, 1)" @keydown="onKeyDown($event, 1)" tabindex="0" <span v-if="range" class="p-slider-handle" :style="rangeEndHandleStyle" @touchstart="onDragStart($event, 1)" @touchmove="onDrag($event)" @touchend="onDragEnd($event)" @mousedown="onMouseDown($event, 1)" @keydown="onKeyDown($event, 1)" :tabindex="tabindex"
role="slider" :aria-valuemin="min" :aria-valuenow="modelValue ? modelValue[1] : null" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy"></span> role="slider" :aria-valuemin="min" :aria-valuenow="modelValue ? modelValue[1] : null" :aria-valuemax="max" :aria-labelledby="ariaLabelledBy" :aria-label="ariaLabel" :aria-orientation="orientation"></span>
</div> </div>
</template> </template>
@ -42,9 +42,17 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
tabindex: {
type: Number,
default: null
},
ariaLabelledBy: { ariaLabelledBy: {
type: String, type: String,
default: null default: null
},
ariaLabel: {
type: String,
default: null
} }
}, },
dragging: false, dragging: false,
@ -176,34 +184,22 @@ export default {
onKeyDown(event, index) { onKeyDown(event, index) {
this.handleIndex = index; this.handleIndex = index;
switch (event.which) { switch (event.which) {
//down //down and left
case 40: case 40:
if (this.vertical) {
this.decrementValue(event, index);
event.preventDefault();
}
break;
//up
case 38:
if (this.vertical) {
this.incrementValue(event, index);
event.preventDefault();
}
break;
//left
case 37: case 37:
if (this.horizontal) { this.decrementValue(event, index);
this.decrementValue(event, index); event.preventDefault();
event.preventDefault();
}
break; break;
//right
//up and right
case 38:
case 39: case 39:
if (this.horizontal) { this.incrementValue(event, index);
this.incrementValue(event, index); event.preventDefault();
event.preventDefault();
}
break; break;
default: default:
break; break;
} }

View File

@ -115,11 +115,23 @@ export default {
<td>false</td> <td>false</td>
<td>When present, it specifies that the component should be disabled.</td> <td>When present, it specifies that the component should be disabled.</td>
</tr> </tr>
<tr>
<td>tabindex</td>
<td>number</td>
<td>null</td>
<td>Index of the element in tabbing order.</td>
</tr>
<tr> <tr>
<td>ariaLabelledBy</td> <td>ariaLabelledBy</td>
<td>string</td> <td>string</td>
<td>null</td> <td>null</td>
<td>Establishes relationships between the component and label(s) where its value should be one or more element IDs.</td> <td>Establishes relationships between the component and label(s) where its value should be one or more element IDs.</td>
</tr>
<tr>
<td>ariaLabel</td>
<td>string</td>
<td>null</td>
<td>Used to define a string that labels the element.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -175,6 +187,73 @@ export default {
</table> </table>
</div> </div>
<h5>Accessibility</h5>
<DevelopmentSection>
<h6>Screen Reader</h6>
<p>Slider element component uses <i>slider</i> role on the handle in addition to the <i>aria-orientation</i>, <i>aria-valuemin</i>, <i>aria-valuemax</i> and <i>aria-valuenow</i> attributes. Value to describe the component can be defined using
<i>aria-labelledby</i> and <i>aria-label</i> props.</p>
<pre v-code><code>
&lt;span id="label_number"&gt;Number&lt;/span&gt;
&lt;Slider aria-labelledby="label_number" /&gt;
&lt;Slider aria-label="Number" /&gt;
</code></pre>
<h6>Keyboard Support</h6>
<div class="doc-tablewrapper">
<table class="doc-table">
<thead>
<tr>
<th>Key</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td><i>tab</i></td>
<td>Moves focus to the slider.</td>
</tr>
<tr>
<td>
<span class="inline-flex flex-column">
<i class="mb-1">left arrow</i>
<i>up arrow</i>
</span>
</td>
<td>Decrements the value.</td>
</tr>
<tr>
<td>
<span class="inline-flex flex-column">
<i class="mb-1">right arrow</i>
<i>down arrow</i>
</span>
</td>
<td>Increments the value.</td>
</tr>
<tr>
<td><i>home</i></td>
<td>Set the minimum value.</td>
</tr>
<tr>
<td><i>end</i></td>
<td>Set the maximum value.</td>
</tr>
<tr>
<td><i>page up</i></td>
<td>Increments the value by 10 steps.</td>
</tr>
<tr>
<td><i>page down</i></td>
<td>Decrements the value by 10 steps.</td>
</tr>
</tbody>
</table>
</div>
</DevelopmentSection>
<h5>Dependencies</h5> <h5>Dependencies</h5>
<p>None.</p> <p>None.</p>
</AppDoc> </AppDoc>