Accessibility: ToggleButton with hidden checkbox

pull/2817/head
Tuğçe Küçükoğlu 2022-07-29 14:47:57 +03:00
parent cb19b3a4b6
commit c68535196c
5 changed files with 232 additions and 14 deletions

View File

@ -34,6 +34,36 @@ const ToggleButtonProps = [
type: "string",
default: "left",
description: 'Position of the icon, valid values are "left" and "right".'
},
{
name: "tabindex",
type: "number",
default: "null",
description: "Index of the element in tabbing order."
},
{
name: "disabled",
type: "boolean",
default: "false",
description: "When present, it specifies that the element should be disabled."
},
{
name: "inputId",
type: "string",
default: "null",
description: "Identifier of the focus input to match a label defined for the chips."
},
{
name: "inputClass",
type: "string",
default: "null",
description: "Style class of the input field."
},
{
name: "inputStyle",
type: "any",
default: "null",
description: "Inline style of the input field."
}
];

View File

@ -31,6 +31,38 @@ export interface ToggleButtonProps {
* Default value is 'left'.
*/
iconPos?: ToggleButtonType;
/**
* When present, it specifies that the element should be disabled.
*/
disabled?: boolean | undefined;
/**
* Index of the element in tabbing order.
*/
tabindex?: string | undefined;
/**
* Identifier of the focus input to match a label defined for the chips.
*/
inputId?: string | undefined;
/**
* Style class of the input field.
*/
inputClass?: any | undefined;
/**
* Inline style of the input field.
*/
inputStyle?: any | undefined;
/**
*
*/
inputProps?: object | undefined;
/**
* Establishes relationships between the component and label(s) where its value should be one or more element IDs.
*/
'aria-labelledby'?: string | undefined;
/**
* Establishes a string value that labels the component.
*/
'aria-label'?: string | undefined;
}
export interface ToggleButtonSlots {

View File

@ -1,6 +1,9 @@
<template>
<div :class="buttonClass" role="button" :tabindex="tabindex" :aria-pressed="modelValue" v-ripple
@click="onClick($event)" @keydown="onKeyDown($event)" @focus="onFocus($event)" @blur="onBlur($event)">
<div :class="buttonClass" @click="onClick($event)" v-ripple>
<span class="p-hidden-accessible">
<input type="checkbox" role="switch" :id="inputId" :class="inputClass" :style="inputStyle" :checked="modelValue" :value="modelValue" :aria-labelledby="ariaLabelledby" :aria-label="ariaLabel"
@focus="onFocus($event)" @blur="onBlur($event)" v-bind="inputProps">
</span>
<span v-if="hasIcon" :class="iconClass"></span>
<span class="p-button-label">{{label}}</span>
</div>
@ -34,7 +37,19 @@ export default {
},
tabindex: {
type: Number,
default: 0
default: null
},
inputId: null,
inputClass: null,
inputStyle: null,
inputProps: null,
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
}
},
methods: {
@ -45,13 +60,6 @@ export default {
this.$emit('click', event);
}
},
onKeyDown(event) {
//space
if (event.keyCode === 32) {
this.onClick(event);
event.preventDefault();
}
},
onFocus(event) {
this.$emit('focus', event);
},
@ -93,3 +101,120 @@ export default {
}
}
</script>
<!-- <template>
<div :class="buttonClass" @click="onClick($event)" v-ripple>
<span class="p-hidden-accessible">
<input type="checkbox" :id="inputId" :name="ariaLabelledby ? ariaLabelledby : ariaLabel ? ariaLabel : null" :checked="modelValue" :value="modelValue" :tabindex="tabindex"
@focus="onFocus($event)" @blur="onBlur($event)">
</span>
<span v-if="hasIcon" :class="iconClass"></span>
<span :for="label" class="p-button-label">{{label}}</span>
</div>
</template>
<script>
import Ripple from 'primevue/ripple';
export default {
name: 'ToggleButton',
emits: ['update:modelValue', 'change', 'click', 'focus', 'blur'],
props: {
modelValue: Boolean,
onIcon: String,
offIcon: String,
onLabel: {
type: String,
default: 'Yes'
},
offLabel: {
type: String,
default: 'No'
},
iconPos: {
type: String,
default: 'left'
},
disabled: {
type: Boolean,
default: false
},
tabindex: {
type: Number,
default: null
},
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
},
inputId: null
},
data() {
return {
focused: null
}
},
methods: {
onClick(event) {
if (!this.disabled) {
this.$emit('update:modelValue', !this.modelValue);
this.$emit('change', event);
this.$emit('click', event);
}
},
onFocus(event) {
this.focused = true;
this.$emit('focus', event);
},
onBlur(event) {
this.focused = false;
this.$emit('blur', event);
}
},
computed: {
buttonClass() {
return ['p-button p-togglebutton p-component', {
'p-focus': this.focused ,
'p-button-icon-only': this.hasIcon && !this.hasLabel,
'p-disabled': this.disabled,
'p-highlight': this.modelValue === true
}]
},
iconClass() {
return [
this.modelValue ? this.onIcon: this.offIcon,
'p-button-icon',
{
'p-button-icon-left': this.iconPos === 'left' && this.label,
'p-button-icon-right': this.iconPos === 'right' && this.label
}
]
},
hasLabel() {
return this.onLabel && this.onLabel.length > 0 && this.offLabel && this.offLabel.length > 0;
},
hasIcon() {
return this.onIcon && this.onIcon.length > 0 && this.offIcon && this.offIcon.length > 0;
},
label() {
return this.hasLabel ? (this.modelValue ? this.onLabel : this.offLabel): '&nbsp;';
}
},
directives: {
'ripple': Ripple
}
}
</script>
<style>
.p-togglebutton.p-button.p-focus {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px #9dc1fb, 0 1px 2px 0 black;
}
</style> -->

View File

@ -14,7 +14,7 @@
<ToggleButton v-model="checked1" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation" />
<h5>Customized</h5>
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation"/>
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="do you confirm" />
</div>
</div>

View File

@ -87,6 +87,36 @@ export default {
<td>string</td>
<td>left</td>
<td>Position of the icon, valid values are "left" and "right".</td>
</tr>
<tr>
<td>disabled</td>
<td>boolean</td>
<td>false</td>
<td>When present, it specifies that the component should be disabled.</td>
</tr>
<tr>
<td>tabindex</td>
<td>number</td>
<td>null</td>
<td>Index of the element in tabbing order.</td>
</tr>
<tr>
<td>inputId</td>
<td>string</td>
<td>null</td>
<td>Style class of the component input field.</td>
</tr>
<tr>
<td>inputClass</td>
<td>string</td>
<td>null</td>
<td>Style class of the input field.</td>
</tr>
<tr>
<td>inputStyle</td>
<td>any</td>
<td>null</td>
<td>Inline style of the input field.</td>
</tr>
</tbody>
</table>
@ -164,6 +194,7 @@ export default {
</code></pre>
<h6>Keyboard Support</h6>
<p>Keyboard interaction is derived from the native browser handling of checkboxs in a group.</p>
<div class="doc-tablewrapper">
<table class="doc-table">
<thead>
@ -205,7 +236,7 @@ export default {
<ToggleButton v-model="checked1" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation" />
<h5>Customized</h5>
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation" />
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="do you confirm" />
</div>
</template>
@ -230,7 +261,7 @@ export default {
<ToggleButton v-model="checked1" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation" />
<h5>Customized</h5>
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation" />
<ToggleButton v-model="checked2" onLabel="I confirm" offLabel="I reject" onIcon="pi pi-check" offIcon="pi pi-times" class="w-full sm:w-10rem" aria-label="do you confirm" />
</div>
</template>
@ -256,7 +287,7 @@ export default {
<p-togglebutton v-model="checked1" on-icon="pi pi-check" off-icon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation"></p-togglebutton>
<h5>Customized</h5>
<p-togglebutton v-model="checked2" on-label="I confirm" off-label="I reject" on-icon="pi pi-check" off-icon="pi pi-times" class="w-full sm:w-10rem" aria-label="Confirmation"></p-togglebutton>
<p-togglebutton v-model="checked2" on-label="I confirm" off-label="I reject" on-icon="pi pi-check" off-icon="pi pi-times" class="w-full sm:w-10rem" aria-label="do you confirm"></p-togglebutton>
</div>
<script type="module">