From c1ee49d6a8ad2fc0ae798b34944ea90059bb70f6 Mon Sep 17 00:00:00 2001 From: Danny Koppenhagen Date: Tue, 5 Dec 2023 09:46:29 +0100 Subject: [PATCH] feat(a11y): support ARIA attributes, role and fallback content for canvas (#1061) --- README.md | 1 + src/chart.ts | 28 ++++++++++++-------- src/props.ts | 12 ++++++++- website/src/api/index.md | 2 ++ website/src/guide/index.md | 52 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8d3dabee..37515ff5 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Need an API to fetch data? Consider [Cube](https://cube.dev/?ref=eco-vue-chartjs - [Reactivity](https://vue-chartjs.org/guide/#updating-charts) - [Access to Chart instance](https://vue-chartjs.org/guide/#access-to-chart-instance) +- [Accessibility](https://vue-chartjs.org/guide/#accessibility) - [Migration from v4 to v5](https://vue-chartjs.org/migration-guides/#migration-from-v4-to-v5/) - [Migration from vue-chart-3](https://vue-chartjs.org/migration-guides/#migration-from-vue-chart-3/) - [API](https://vue-chartjs.org/api/) diff --git a/src/chart.ts b/src/chart.ts index 01607b34..033c4356 100644 --- a/src/chart.ts +++ b/src/chart.ts @@ -1,15 +1,16 @@ +import { Chart as ChartJS } from 'chart.js' import { defineComponent, - ref, - shallowRef, h, - onMounted, + nextTick, onBeforeUnmount, - watch, + onMounted, + ref, + shallowRef, toRaw, - nextTick + watch } from 'vue' -import { Chart as ChartJS } from 'chart.js' + import type { ChartComponent } from './types.js' import { Props } from './props.js' import { @@ -23,7 +24,7 @@ import { export const Chart = defineComponent({ props: Props, - setup(props, { expose }) { + setup(props, { expose, slots }) { const canvasRef = ref(null) const chartRef = shallowRef(null) @@ -112,9 +113,16 @@ export const Chart = defineComponent({ ) return () => { - return h('canvas', { - ref: canvasRef - }) + return h( + 'canvas', + { + role: 'img', + ariaLabel: props.ariaLabel, + ariaDescribedby: props.ariaDescribedby, + ref: canvasRef + }, + [h('p', {}, [slots.default ? slots.default() : ''])] + ) } } }) as ChartComponent diff --git a/src/props.ts b/src/props.ts index 877b243e..a17254f7 100644 --- a/src/props.ts +++ b/src/props.ts @@ -30,10 +30,20 @@ export const CommonProps = { } } as const +export const A11yProps = { + ariaLabel: { + type: String + }, + ariaDescribedby: { + type: String + } +} as const + export const Props = { type: { type: String as PropType, required: true }, - ...CommonProps + ...CommonProps, + ...A11yProps } as const diff --git a/website/src/api/index.md b/website/src/api/index.md index e1547ab2..6c126a95 100644 --- a/website/src/api/index.md +++ b/website/src/api/index.md @@ -11,6 +11,8 @@ Some basic props are defined in the components provided by `vue-chartjs`. | datasetIdKey | Key name to identify the dataset | | plugins | Plugins array that is passed into the Chart.js chart | | updateMode | Mode string to indicate the transition configuration to be used. | +| ariaLabel | An [ARIA label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) that describes the chart to make it accessible. | +| ariaDescribedby | A reference to the [describing element](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby). E. g. a table representation of the data. | The rest of the props will fall through to the canvas element. diff --git a/website/src/guide/index.md b/website/src/guide/index.md index e97dad3d..ac9dd416 100644 --- a/website/src/guide/index.md +++ b/website/src/guide/index.md @@ -141,6 +141,58 @@ In Vue3 projects: const chartInstance = this.$refs.bar.chart ``` +## Accessibility + +To make your charts accessible to all users, you should label your charts. +Please refer also to the official [Chart.js Accessibility notes](https://www.chartjs.org/docs/latest/general/accessibility.html). + +### `aria-label` + +You can directly label a chart by passing an `aria-label` prop. + +```vue + +``` + +### `aria-describedby` + +You can reference to a describing element such as a table which describes the data by using the `aria-describedby` property. + +```vue + +``` + +### Fallback-Content + +In case the Browser is not able to render the `canvas` element, you should consider providing fallback content by using the Slot of each component. + +```vue + +``` + ## Examples ### Chart with props