Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CED-1815 Fix accessibility issue in v3 EsRadioButton #1506

Merged
merged 18 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions es-ds-components/components/es-radio-button-group.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script setup lang="ts">
/*
Similar API to https://bootstrap-vue.org/docs/components/form-radio#component-reference

Only more restrained, and based on historical usage. Things like configurable
value field aren't supported as they weren't used downstream.

Similarly things like size are not implemented
*/

interface IOptions {
text: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
disabled?: boolean;
}

interface IProps {
id: string;
label: string;
name?: string;
options?: IOptions[];
inline?: boolean;
}

withDefaults(defineProps<IProps>(), {
inline: false,
name: '',
options: undefined,
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const model = defineModel<any>();
</script>

<template>
<fieldset
:id="id"
class="form-group">
<legend
:id="`legend-${id}`"
class="font-size-100"
tabindex="-1">
{{ label }}
</legend>
<template v-if="$slots.default">
<slot />
</template>
<template v-else>
<es-radio-button
v-for="option in options"
:key="option.value"
v-model="model"
:disabled="option?.disabled || false"
:display-name="option?.text"
:group-name="name"
:inline="inline"
:value="option.value" />
</template>
</fieldset>
</template>
57 changes: 25 additions & 32 deletions es-ds-components/components/es-radio-button.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
<script setup lang="ts">
import RadioButton from 'primevue/radiobutton';

defineProps({
disabled: {
type: Boolean,
default: false,
},
displayName: {
type: String,
required: true,
default: '',
},
groupName: {
type: String,
required: false,
default: '',
},
inline: {
type: Boolean,
default: false,
},
value: {
type: String,
required: true,
default: '',
},
interface IProps {
disabled?: boolean;
displayName: string;
groupName?: string;
inline?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
}

const props = withDefaults(defineProps<IProps>(), {
disabled: false,
displayName: '',
groupName: '',
inline: false,
value: null,
});
</script>

Expand All @@ -35,13 +26,15 @@ defineProps({
input-class="custom-radio-input"
v-bind="$attrs"
:disabled="disabled"
:input-id="`${value}-${groupName}`"
:name="groupName"
:value="value" />
<label
:for="`${value}-${groupName}`"
class="custom-control-label"
>{{ displayName }}</label
>
:input-id="`${props.value}-${props.groupName}`"
:name="props.groupName"
:value="props.value" />
<slot>
<label
:for="`${props.value}-${props.groupName}`"
class="custom-control-label">
{{ props.displayName }}
</label>
</slot>
</div>
</template>
19 changes: 17 additions & 2 deletions es-ds-docs/components/ds-doc-source.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default {
default: '',
required: false,
},
compTitle: {
type: String,
default: 'Component source',
required: false,
},
docCode: {
type: String,
default: '',
Expand All @@ -22,17 +27,25 @@ export default {
default: '',
required: false,
},
docTitle: {
type: String,
default: 'Documentation source',
required: false,
},
},
};
</script>

<template>
<div class="mt-500">
<client-only>
<es-collapse
v-if="compCode"
id="compsource">
<template #title>
<h2 class="mb-0">Component source</h2>
<h2 class="mb-0">
{{ compTitle }}
</h2>
</template>
<ds-code-block
:code="compCode"
Expand All @@ -43,7 +56,9 @@ export default {
id="docsource"
class="mt-500">
<template #title>
<h2 class="mb-0">Documentation source</h2>
<h2 class="mb-0">
{{ docTitle }}
</h2>
</template>
<ds-code-block
:code="docCode"
Expand Down
159 changes: 111 additions & 48 deletions es-ds-docs/pages/molecules/radio-button.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
<script setup lang="ts">
import { ref } from 'vue';

const selectedFruit = ref('banana');
const fruits = ref([
{ name: 'Apple', key: 'apple' },
{ name: 'Banana', key: 'banana' },
{ name: 'Cherry', key: 'cherry' },
]);
const test2Options = [
{ text: 'Toggle this custom radio', value: 'first' },
{ text: 'Or toggle this other custom radio', value: 'second' },
{ text: 'This one is Disabled', value: 'third', disabled: true },
{ text: 'This is the 4th radio', value: { fourth: 4 } },
];
const test2Selected = ref('first');

const { $prism } = useNuxtApp();
const compCode = ref('');
const docCode = ref('');

if ($prism) {
/* eslint-disable import/no-webpack-loader-syntax, import/no-self-import */
const compSource = await import('@energysage/es-ds-components/components/es-radio-button.vue?raw');
const docSource = await import('./radio-button.vue?raw');
/* eslint-enable import/no-webpack-loader-syntax, import/no-self-import */

compCode.value = $prism.normalizeCode(compSource.default);
docCode.value = $prism.normalizeCode(docSource.default);
$prism.highlight();
}

const propTableRows = [
// Name, Type, Default, Description
const radioButtonPropTableRows = [
['disabled', 'Boolean', 'false', 'When present, it specifies that the radio button should be disabled.'],
['inline', 'Boolean', 'false', 'When present, it specifies that the radio buttons should be displayed inline.'],
['v-model', 'String', 'n/a', 'Required. The v-model directive binds the radio button to a data property.'],
['v-model', 'Any', 'n/a', 'Required. The v-model directive binds the radio button to a data property.'],
['display-name', 'String', 'n/a', 'Required. The text to display next to the radio button.'],
['value', 'String', 'n/a', 'Required. The value to be used by v-model.'],
['value', 'Any', 'n/a', 'Required. The value to be used by v-model.'],
[
'group-name',
'String',
Expand All @@ -35,11 +30,44 @@ const propTableRows = [
'use the same v-model.',
],
];

// Name, Type, Default, Description
const radioButtonGroupPropTableRows = [
['id', 'String', 'n/a', 'Required.'],
['label', 'String', 'n/a', 'Required. Corresponds to the legend value describing the group'],
['name', 'String', `''`, 'Optional. Maps to the group name for each radio input'],
[
'options',
'Array',
'undefined',
'Optional. Must provide if not using the default slot. Allows radio input values to be passed as an array.',
],
['inline', 'Boolean', 'false', 'Optional. Attribute is passed down to children when using the options attribute'],
];

const { $prism } = useNuxtApp();
const radioButtonComponentCode = ref('');
const radioButtonGroupComponentCode = ref('');
const docCode = ref('');
if ($prism) {
/* eslint-disable import/no-webpack-loader-syntax, import/no-self-import */
const radioButtonComponentSource = await import('@energysage/es-ds-components/components/es-radio-button.vue?raw');
// eslint-disable-next-line max-len
const radioButtonGroupComponentSource = await import(
'@energysage/es-ds-components/components/es-radio-button-group.vue?raw'
);
const docSource = await import('./radio-button.vue?raw');
/* eslint-enable import/no-webpack-loader-syntax, import/no-self-import */

radioButtonComponentCode.value = $prism.normalizeCode(radioButtonComponentSource.default);
radioButtonGroupComponentCode.value = $prism.normalizeCode(radioButtonGroupComponentSource.default);
docCode.value = $prism.normalizeCode(docSource.default);
$prism.highlight();
}
</script>

<template>
<div>
<h1>Radio button</h1>
<p class="mb-500">
Uses
<a
Expand All @@ -51,50 +79,85 @@ const propTableRows = [

<div class="my-500">
<h2>Inline</h2>
<p>Please choose your favorite fruit.</p>
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="inline"
:value="fruit.key"
inline />
<es-radio-button-group
id="idFruits"
label="Please choose your favorite fruit.">
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="inline"
:value="fruit.key"
inline />
</es-radio-button-group>
</div>

<div class="my-500">
<h2>Stacked</h2>
<p>Please choose your favorite fruit.</p>
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="stacked"
:value="fruit.key" />
<es-radio-button-group
id="idFruits2"
label="Please choose your favorite fruit.">
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="stacked"
:value="fruit.key" />
</es-radio-button-group>
</div>

<div class="my-500">
<h2>Disabled</h2>
<p>Please choose your favorite fruit.</p>
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="disabled"
:value="fruit.key"
disabled />
<es-radio-button-group
id="idFruits3"
label="Please choose your favorite fruit.">
<es-radio-button
v-for="fruit in fruits"
:key="fruit.key"
v-model="selectedFruit"
:display-name="fruit.name"
group-name="disabled"
:value="fruit.key"
disabled />
</es-radio-button-group>
</div>

<div class="my-500">
<h2>Using options prop</h2>
<es-radio-button-group
id="test2RadioGroup"
v-model="test2Selected"
label="Radios using options"
:options="test2Options"
name="test2" />

<div>selected: {{ test2Selected }}</div>
</div>

<div class="mb-500">
<h2>EsRadioButton props</h2>
<ds-prop-table :rows="propTableRows" />
<ds-prop-table :rows="radioButtonPropTableRows" />
</div>

<div class="mb-500">
<h2>EsRadioButtonGroup props</h2>
<ds-prop-table :rows="radioButtonGroupPropTableRows" />
</div>

<ds-doc-source
:comp-code="compCode"
comp-source="es-ds-components/src/lib-components/es-radio-button.vue"
comp-title="Radio button component"
:comp-code="radioButtonComponentCode"
comp-source="es-ds-components/src/lib-components/es-radio-button.vue" />

<ds-doc-source
comp-title="Radio button group component"
:comp-code="radioButtonGroupComponentCode"
comp-source="es-ds-components/src/lib-components/es-radio-button-group.vue" />

<ds-doc-source
doc-title="Radio button & radio button group documentation"
:doc-code="docCode"
doc-source="es-ds-docs/pages/molecules/radio-button.vue" />
</div>
Expand Down
Loading