Skip to content

Commit 815cf39

Browse files
authored
feat(a11y): improve chart figure (#1104)
1 parent 74df29b commit 815cf39

7 files changed

Lines changed: 148 additions & 32 deletions

File tree

.playground/playground.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,32 @@
1919

2020
import React from 'react';
2121

22-
import { Chart, BarSeries, ScaleType, LineAnnotation, AnnotationDomainTypes, LineAnnotationDatum } from '../src';
22+
import { Chart, AreaSeries, LineSeries, BarSeries, ScaleType } from '../src';
2323

24-
function generateAnnotationData(values: any[]): LineAnnotationDatum[] {
25-
return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` }));
26-
}
2724
export class Playground extends React.Component {
2825
render() {
2926
return (
3027
<div className="App">
3128
<Chart size={[500, 200]}>
32-
<LineAnnotation
33-
domainType={AnnotationDomainTypes.XDomain}
34-
id="ann"
35-
dataValues={[{ dataValue: 'bags' }]}
36-
marker={<div style={{ background: 'red' }}>hello</div>}
37-
// markerPosition="top"
29+
<AreaSeries
30+
id="lines"
31+
name="test2"
32+
data={[
33+
{ x: 'trousers', y: 300, val: 1232 },
34+
{ x: 'watches', y: 20, val: 1232 },
35+
{ x: 'bags', y: 700, val: 1232 },
36+
{ x: 'cocktail dresses', y: 804, val: 1232 },
37+
]}
3838
/>
39-
<LineAnnotation
40-
domainType={AnnotationDomainTypes.YDomain}
41-
id="ann1"
42-
dataValues={generateAnnotationData([30])}
43-
marker={<div style={{ background: 'yellow' }}>Horizontal</div>}
44-
// markerPosition="right"
39+
<LineSeries
40+
id="lines2"
41+
name="test"
42+
data={[
43+
{ x: 'trousers', y: 300, val: 1232 },
44+
{ x: 'watches', y: 20, val: 1232 },
45+
{ x: 'bags', y: 700, val: 1232 },
46+
{ x: 'cocktail dresses', y: 804, val: 1232 },
47+
]}
4548
/>
4649
<BarSeries
4750
id="bars"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { common } from '../page_objects/common';
21+
22+
describe('Accessibility tree', () => {
23+
it('should include the series types if one type of series', async () => {
24+
const tree = await common.testAccessibilityTree(
25+
'http://localhost:9001/iframe.html?id=annotations-lines--x-continuous-domain',
26+
'.echCanvasRenderer',
27+
);
28+
// the legend has bars and lines as value.descriptions not value.name
29+
const hasTextOfChartTypes = tree.children.filter((value) => {
30+
return value.name === 'bar chart';
31+
});
32+
expect(hasTextOfChartTypes[0].name).toBe('bar chart');
33+
});
34+
it('should include the series types if multiple types of series', async () => {
35+
const tree = await common.testAccessibilityTree(
36+
'http://localhost:9001/iframe.html?id=mixed-charts--bars-and-lines',
37+
'.echCanvasRenderer',
38+
);
39+
// the legend has bars and lines as value.descriptions not value.name
40+
const hasTextOfChartTypes = tree.children.filter((value) => {
41+
return value.name === 'Mixed chart: bar and line chart';
42+
});
43+
expect(hasTextOfChartTypes[0].name).toBe('Mixed chart: bar and line chart');
44+
});
45+
});

src/chart_types/xy_chart/renderer/canvas/xy_chart.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,13 @@ import {
4848
import { computeSeriesGeometriesSelector } from '../../state/selectors/compute_series_geometries';
4949
import { getAxesStylesSelector } from '../../state/selectors/get_axis_styles';
5050
import { getHighlightedSeriesSelector } from '../../state/selectors/get_highlighted_series';
51+
import { getSeriesTypes } from '../../state/selectors/get_series_types';
5152
import { getAnnotationSpecsSelector, getAxisSpecsSelector } from '../../state/selectors/get_specs';
5253
import { isChartEmptySelector } from '../../state/selectors/is_chart_empty';
5354
import { Geometries, Transform } from '../../state/utils/types';
5455
import { LinesGrid } from '../../utils/grid_lines';
5556
import { IndexedGeometryMap } from '../../utils/indexed_geometry_map';
56-
import { AxisSpec, AnnotationSpec } from '../../utils/specs';
57+
import { AxisSpec, AnnotationSpec, SeriesType } from '../../utils/specs';
5758
import { renderXYChartCanvas2d } from './renderers';
5859

5960
/** @internal */
@@ -76,6 +77,7 @@ export interface ReactiveChartStateProps {
7677
annotationDimensions: Map<AnnotationId, AnnotationDimensions>;
7778
annotationSpecs: AnnotationSpec[];
7879
panelGeoms: PanelGeoms;
80+
seriesTypes: Set<SeriesType>;
7981
}
8082

8183
interface ReactiveChartDispatchProps {
@@ -152,27 +154,37 @@ class XYChartComponent extends React.Component<XYChartProps> {
152154
initialized,
153155
isChartEmpty,
154156
chartContainerDimensions: { width, height },
157+
seriesTypes,
155158
} = this.props;
156159

157160
if (!initialized || isChartEmpty) {
158161
this.ctx = null;
159162
return null;
160163
}
161164

165+
const chartSeriesTypes =
166+
seriesTypes.size > 1 ? `Mixed chart: ${[...seriesTypes].join(' and ')} chart` : `${[...seriesTypes]} chart`;
167+
162168
return (
163-
<canvas
164-
ref={forwardStageRef}
165-
className="echCanvasRenderer"
166-
width={width * this.devicePixelRatio}
167-
height={height * this.devicePixelRatio}
168-
style={{
169-
width,
170-
height,
171-
}}
172-
aria-label="Chart"
173-
// eslint-disable-next-line jsx-a11y/no-interactive-element-to-noninteractive-role
174-
role="img"
175-
/>
169+
<figure>
170+
<canvas
171+
ref={forwardStageRef}
172+
className="echCanvasRenderer"
173+
width={width * this.devicePixelRatio}
174+
height={height * this.devicePixelRatio}
175+
style={{
176+
width,
177+
height,
178+
}}
179+
// eslint-disable-next-line jsx-a11y/no-interactive-element-to-noninteractive-role
180+
role="presentation"
181+
>
182+
<dl className="echScreen-reader">
183+
<dt>Chart type</dt>
184+
<dd>{chartSeriesTypes}</dd>
185+
</dl>
186+
</canvas>
187+
</figure>
176188
);
177189
}
178190
}
@@ -224,6 +236,7 @@ const DEFAULT_PROPS: ReactiveChartStateProps = {
224236
annotationDimensions: new Map(),
225237
annotationSpecs: [],
226238
panelGeoms: [],
239+
seriesTypes: new Set(),
227240
};
228241

229242
const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => {
@@ -252,6 +265,7 @@ const mapStateToProps = (state: GlobalChartState): ReactiveChartStateProps => {
252265
annotationDimensions: computeAnnotationDimensionsSelector(state),
253266
annotationSpecs: getAnnotationSpecsSelector(state),
254267
panelGeoms: computePanelsSelectors(state),
268+
seriesTypes: getSeriesTypes(state),
255269
};
256270
};
257271

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
@import 'highlighter';
22
@import 'crosshair';
3+
@import 'screen_reader';
34
@import 'annotations/index';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.echScreenReaderOnly {
2+
position: absolute;
3+
left: -10000px;
4+
top: auto;
5+
width: 1px;
6+
height: 1px;
7+
overflow: hidden;
8+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import createCachedSelector from 're-reselect';
21+
22+
import { getChartIdSelector } from '../../../../state/selectors/get_chart_id';
23+
import { SeriesType } from '../../utils/specs';
24+
import { getSeriesSpecsSelector } from './get_specs';
25+
26+
/** @internal */
27+
export const getSeriesTypes = createCachedSelector(
28+
[getSeriesSpecsSelector],
29+
(specs): Set<SeriesType> => {
30+
const seriesTypes = new Set<SeriesType>();
31+
specs.forEach((value) => seriesTypes.add(value.seriesType));
32+
return seriesTypes;
33+
},
34+
)(getChartIdSelector);

src/components/__snapshots__/chart.test.tsx.snap

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,19 @@ exports[`Chart should render the legend name test 1`] = `
7272
</Crosshair>
7373
</Connect(Crosshair)>
7474
<Connect(XYChart) forwardStageRef={{...}}>
75-
<XYChart forwardStageRef={{...}} initialized={true} isChartEmpty={false} debug={true} geometries={{...}} geometriesIndex={{...}} theme={{...}} chartContainerDimensions={{...}} highlightedLegendItem={[undefined]} rotation={0} renderingArea={{...}} chartTransform={{...}} axesSpecs={{...}} perPanelAxisGeoms={{...}} perPanelGridLines={{...}} axesStyles={{...}} annotationDimensions={{...}} annotationSpecs={{...}} panelGeoms={{...}} onChartRendered={[Function (anonymous)]}>
76-
<canvas className=\\"echCanvasRenderer\\" width={150} height={200} style={{...}} aria-label=\\"Chart\\" role=\\"img\\" />
75+
<XYChart forwardStageRef={{...}} initialized={true} isChartEmpty={false} debug={true} geometries={{...}} geometriesIndex={{...}} theme={{...}} chartContainerDimensions={{...}} highlightedLegendItem={[undefined]} rotation={0} renderingArea={{...}} chartTransform={{...}} axesSpecs={{...}} perPanelAxisGeoms={{...}} perPanelGridLines={{...}} axesStyles={{...}} annotationDimensions={{...}} annotationSpecs={{...}} panelGeoms={{...}} seriesTypes={{...}} onChartRendered={[Function (anonymous)]}>
76+
<figure>
77+
<canvas className=\\"echCanvasRenderer\\" width={150} height={200} style={{...}} role=\\"presentation\\">
78+
<dl className=\\"echScreen-reader\\">
79+
<dt>
80+
Chart type
81+
</dt>
82+
<dd>
83+
bar chart
84+
</dd>
85+
</dl>
86+
</canvas>
87+
</figure>
7788
</XYChart>
7889
</Connect(XYChart)>
7990
<Connect(Tooltip) getChartContainerRef={[Function (anonymous)]}>

0 commit comments

Comments
 (0)