Skip to content

Commit 7f5e761

Browse files
committed
add support for pie chart segment aria label
1 parent 4f20a25 commit 7f5e761

File tree

11 files changed

+117
-72
lines changed

11 files changed

+117
-72
lines changed

src/__tests__/__snapshots__/documenter.test.ts.snap

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Supported Highcharts versions:
126126
"description": "An object that contains all of the localized strings required by the component.",
127127
"i18nTag": true,
128128
"inlineType": {
129-
"name": "BaseI18nStrings",
129+
"name": "CartesianI18nStrings",
130130
"properties": [
131131
{
132132
"name": "chartAccessibleDescription",
@@ -188,7 +188,7 @@ Supported Highcharts versions:
188188
},
189189
"name": "i18nStrings",
190190
"optional": true,
191-
"type": "BaseI18nStrings",
191+
"type": "CartesianI18nStrings",
192192
},
193193
{
194194
"defaultValue": "false",
@@ -276,7 +276,7 @@ Supported types:
276276
* [area](https://api.highcharts.com/highcharts/series.area).
277277
* [areaspline](https://api.highcharts.com/highcharts/series.areaspline).
278278
* [column](https://api.highcharts.com/highcharts/series.column).
279-
* [errorbar](https://api.highcharts.com/highcharts/series.errorbar).
279+
* [errorbar](https://api.highcharts.com/highcharts/series.errorbar) - requires "highcharts/highcharts-more" module.
280280
* [line](https://api.highcharts.com/highcharts/series.line).
281281
* [scatter](https://api.highcharts.com/highcharts/series.scatter).
282282
* [spline](https://api.highcharts.com/highcharts/series.spline).
@@ -637,7 +637,7 @@ Supported Highcharts versions:
637637
"description": "An object that contains all of the localized strings required by the component.",
638638
"i18nTag": true,
639639
"inlineType": {
640-
"name": "BaseI18nStrings",
640+
"name": "PieI18nStrings",
641641
"properties": [
642642
{
643643
"name": "chartAccessibleDescription",
@@ -670,27 +670,22 @@ Supported Highcharts versions:
670670
"type": "string",
671671
},
672672
{
673-
"name": "seriesFilterLabel",
674-
"optional": true,
675-
"type": "string",
676-
},
677-
{
678-
"name": "seriesFilterPlaceholder",
673+
"name": "segmentAccessibleDescription",
679674
"optional": true,
680675
"type": "string",
681676
},
682677
{
683-
"name": "seriesFilterSelectedAriaLabel",
678+
"name": "seriesFilterLabel",
684679
"optional": true,
685680
"type": "string",
686681
},
687682
{
688-
"name": "xAxisAccessibleDescription",
683+
"name": "seriesFilterPlaceholder",
689684
"optional": true,
690685
"type": "string",
691686
},
692687
{
693-
"name": "yAxisAccessibleDescription",
688+
"name": "seriesFilterSelectedAriaLabel",
694689
"optional": true,
695690
"type": "string",
696691
},
@@ -699,7 +694,7 @@ Supported Highcharts versions:
699694
},
700695
"name": "i18nStrings",
701696
"optional": true,
702-
"type": "BaseI18nStrings",
697+
"type": "PieI18nStrings",
703698
},
704699
{
705700
"description": "Description of the inner chart area. Only supported for donut series.",

src/cartesian-chart/interfaces.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface CartesianChartProps extends CoreTypes.BaseChartOptions, CoreTyp
2929
* * [area](https://api.highcharts.com/highcharts/series.area).
3030
* * [areaspline](https://api.highcharts.com/highcharts/series.areaspline).
3131
* * [column](https://api.highcharts.com/highcharts/series.column).
32-
* * [errorbar](https://api.highcharts.com/highcharts/series.errorbar).
32+
* * [errorbar](https://api.highcharts.com/highcharts/series.errorbar) - requires "highcharts/highcharts-more" module.
3333
* * [line](https://api.highcharts.com/highcharts/series.line).
3434
* * [scatter](https://api.highcharts.com/highcharts/series.scatter).
3535
* * [spline](https://api.highcharts.com/highcharts/series.spline).
@@ -106,6 +106,12 @@ export interface CartesianChartProps extends CoreTypes.BaseChartOptions, CoreTyp
106106
* A callback, triggered when series visibility changes as result of user interacting with the legend or filter.
107107
*/
108108
onChangeVisibleSeries?: NonCancelableEventHandler<{ visibleSeries: string[] }>;
109+
110+
/**
111+
* An object that contains all of the localized strings required by the component.
112+
* @i18n
113+
*/
114+
i18nStrings?: CoreTypes.CartesianI18nStrings;
109115
}
110116

111117
export namespace CartesianChartProps {

src/core/__tests__/chart-core-axes.test.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,7 @@ function optionsContaining({
6262
}) {
6363
return objectContainingDeep({
6464
options: {
65-
xAxis: [
66-
{
67-
title: { text: xTitle },
68-
},
69-
],
65+
xAxis: [{ title: { text: xTitle } }],
7066
yAxis: [
7167
{
7268
title: { text: yTitle },

src/core/__tests__/chart-core-navigation-pie.test.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe("CoreChart: navigation, pie charts", () => {
8787
focusApplication();
8888
keyDown(keyCode);
8989

90-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
90+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
9191
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
9292
});
9393

@@ -103,7 +103,7 @@ describe("CoreChart: navigation, pie charts", () => {
103103
focusApplication();
104104
keyDown(keyCode);
105105

106-
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series.[true,false]");
106+
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series[true,false]");
107107
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P3Pie series60");
108108
});
109109

@@ -117,22 +117,22 @@ describe("CoreChart: navigation, pie charts", () => {
117117
focusApplication();
118118
keyDown(KeyCode.home);
119119

120-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
120+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
121121
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
122122

123123
keyDown(keyCode);
124124

125-
expect(describeFocusedElement()).toBe("button:P2, 30. Pie series.[true,false]");
125+
expect(describeFocusedElement()).toBe("button:P2, 30. Pie series[true,false]");
126126
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P2Pie series30");
127127

128128
keyDown(keyCode);
129129

130-
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series.[true,false]");
130+
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series[true,false]");
131131
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P3Pie series60");
132132

133133
keyDown(keyCode);
134134

135-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
135+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
136136
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
137137
});
138138

@@ -146,22 +146,22 @@ describe("CoreChart: navigation, pie charts", () => {
146146
focusApplication();
147147
keyDown(KeyCode.end);
148148

149-
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series.[true,false]");
149+
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series[true,false]");
150150
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P3Pie series60");
151151

152152
keyDown(keyCode);
153153

154-
expect(describeFocusedElement()).toBe("button:P2, 30. Pie series.[true,false]");
154+
expect(describeFocusedElement()).toBe("button:P2, 30. Pie series[true,false]");
155155
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P2Pie series30");
156156

157157
keyDown(keyCode);
158158

159-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
159+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
160160
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
161161

162162
keyDown(keyCode);
163163

164-
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series.[true,false]");
164+
expect(describeFocusedElement()).toBe("button:P3, 60. Pie series[true,false]");
165165
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P3Pie series60");
166166
});
167167

@@ -171,7 +171,7 @@ describe("CoreChart: navigation, pie charts", () => {
171171
focusApplication();
172172
keyDown(KeyCode.enter);
173173

174-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
174+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
175175
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
176176

177177
keyDown(KeyCode.escape);
@@ -186,7 +186,7 @@ describe("CoreChart: navigation, pie charts", () => {
186186
focusApplication();
187187
keyDown(KeyCode.enter);
188188

189-
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series.[true,false]");
189+
expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]");
190190
expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10");
191191
expect(wrapper.findTooltip()!.findDismissButton()).toBe(null);
192192

src/core/chart-api/chart-extra-context.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import type Highcharts from "highcharts";
55

6+
import { ChartLabels } from "../i18n-utils";
67
import { ChartHighlightProps, CoreLegendItem, Rect } from "../interfaces";
78
import { getGroupRect, isSeriesStacked } from "../utils";
89

@@ -27,6 +28,7 @@ export namespace ChartExtraContext {
2728
legendEnabled: boolean;
2829
tooltipEnabled: boolean;
2930
keyboardNavigationEnabled: boolean;
31+
labels: ChartLabels;
3032
}
3133

3234
export interface Handlers {
@@ -57,6 +59,7 @@ export function createChartContext(): ChartExtraContext {
5759
legendEnabled: false,
5860
tooltipEnabled: false,
5961
keyboardNavigationEnabled: false,
62+
labels: {},
6063
},
6164
handlers: {},
6265
state: {},

src/core/chart-api/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,9 @@ export class ChartAPI {
237237
this.chartExtraNavigation.announceElement(getGroupAccessibleDescription(group), false);
238238
},
239239
onFocusPoint: (point: Highcharts.Point) => {
240+
const labels = this.context.settings.labels;
240241
this.highlightActions(point, true);
241-
this.chartExtraNavigation.announceElement(getPointAccessibleDescription(point), false);
242+
this.chartExtraNavigation.announceElement(getPointAccessibleDescription(point, labels), false);
242243
},
243244
onBlur: () => this.clearChartHighlight(),
244245
onActivateGroup: () => {
@@ -251,8 +252,9 @@ export class ChartAPI {
251252
onActivatePoint: () => {
252253
const current = this.chartExtraTooltip.get();
253254
if (current.point) {
255+
const labels = this.context.settings.labels;
254256
this.chartExtraTooltip.pinTooltip();
255-
this.chartExtraNavigation.announceElement(getPointAccessibleDescription(current.point), true);
257+
this.chartExtraNavigation.announceElement(getPointAccessibleDescription(current.point, labels), true);
256258
}
257259
},
258260
};

src/core/chart-core.tsx

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import HighchartsReact from "highcharts-react-official";
88

99
import { getIsRtl, useMergeRefs, useUniqueId } from "@cloudscape-design/component-toolkit/internal";
1010
import { isDevelopment } from "@cloudscape-design/component-toolkit/internal";
11-
import { useInternalI18n } from "@cloudscape-design/components/internal/do-not-use/i18n";
1211
import Spinner from "@cloudscape-design/components/spinner";
1312

1413
import { getDataAttributes } from "../internal/base-component/get-data-attributes";
@@ -25,8 +24,9 @@ import { ChartFooter, ChartHeader } from "./components/core-slots";
2524
import { ChartTooltip } from "./components/core-tooltip";
2625
import { VerticalAxisTitle } from "./components/core-vertical-axis-title";
2726
import { getFormatter } from "./formatters";
27+
import { useChartI18n } from "./i18n-utils";
2828
import { CoreChartProps } from "./interfaces";
29-
import { getFormattedPointDescription } from "./utils";
29+
import { getPointAccessibleDescription } from "./utils";
3030

3131
import styles from "./styles.css.js";
3232
import testClasses from "./test-classes/styles.css.js";
@@ -65,18 +65,18 @@ export function InternalCoreChart({
6565
...rest
6666
}: CoreChartProps & InternalBaseComponentProps) {
6767
const highcharts = rest.highcharts as null | typeof Highcharts;
68-
const i18n = useInternalI18n("[charts]");
69-
70-
const settings = {
68+
const labels = useChartI18n({ ariaLabel, ariaDescription, i18nStrings });
69+
const context = {
7170
chartId: useUniqueId(),
7271
noDataEnabled: !!noDataOptions,
7372
legendEnabled: legendOptions?.enabled !== false,
7473
tooltipEnabled: tooltipOptions?.enabled !== false,
7574
keyboardNavigationEnabled: keyboardNavigation,
75+
labels,
7676
};
7777
const handlers = { onHighlight, onClearHighlight, onVisibleItemsChange };
7878
const state = { visibleItems };
79-
const api = useChartAPI(settings, handlers, state);
79+
const api = useChartAPI(context, handlers, state);
8080

8181
const rootClassName = clsx(testClasses.root, styles.root, fitHeight && styles["root-fit-height"], className);
8282
const rootRef = useRef<HTMLDivElement>(null);
@@ -109,15 +109,6 @@ export function InternalCoreChart({
109109
);
110110
}
111111

112-
const aria = {
113-
chartLabel: ariaLabel,
114-
chartDescription: ariaDescription,
115-
// We reuse existing translations as passing for now, but it is possible to introduce new i18n strings that
116-
// are parametrized with axes names, and more.
117-
chartContainerLabel: i18n("i18nStrings.chartAriaRoleDescription", i18nStrings?.chartAccessibleDescription),
118-
chartXAxisLabel: i18n("i18nStrings.xAxisAriaRoleDescription", i18nStrings?.xAxisAccessibleDescription),
119-
chartYAxisLabel: i18n("i18nStrings.yAxisAriaRoleDescription", i18nStrings?.yAxisAccessibleDescription),
120-
};
121112
const apiOptions = api.getOptions();
122113
const inverted = !!options.chart?.inverted;
123114
const isRtl = () => getIsRtl(rootRef?.current);
@@ -182,18 +173,22 @@ export function InternalCoreChart({
182173
accessibility: {
183174
// The default chart title is disabled by default to prevent the default "Chart" in the screen-reader detail.
184175
defaultChartTitle: "",
185-
chartContainerLabel: aria.chartLabel,
186-
svgContainerLabel: aria.chartContainerLabel,
176+
chartContainerLabel: labels.chartLabel,
177+
svgContainerLabel: labels.chartContainerLabel,
187178
// We used {names[0]} to inject the axis title, see: https://api.highcharts.com/highcharts/lang.accessibility.axis.xAxisDescriptionSingular.
188179
axis: {
189-
xAxisDescriptionSingular: aria.chartXAxisLabel ? `${aria.chartXAxisLabel}, {names[0]}` : undefined,
190-
yAxisDescriptionSingular: aria.chartYAxisLabel ? `${aria.chartYAxisLabel}, {names[0]}` : undefined,
180+
xAxisDescriptionSingular: labels.chartXAxisLabel
181+
? `${labels.chartXAxisLabel}, {names[0]}`
182+
: undefined,
183+
yAxisDescriptionSingular: labels.chartYAxisLabel
184+
? `${labels.chartYAxisLabel}, {names[0]}`
185+
: undefined,
191186
},
192187
...options.lang?.accessibility,
193188
},
194189
},
195190
accessibility: {
196-
description: aria.chartDescription,
191+
description: labels.chartDescription,
197192
screenReaderSection: {
198193
// We use custom before-message to remove detail that are currently not supported with our i18n.
199194
// See: https://api.highcharts.com/highcharts/accessibility.screenReaderSection.beforeChartFormat.
@@ -206,7 +201,7 @@ export function InternalCoreChart({
206201
keyboardNavigation: options.accessibility?.keyboardNavigation ?? { enabled: !keyboardNavigation },
207202
point: {
208203
// Point description formatter is overridden to respect custom axes value formatters.
209-
descriptionFormatter: getFormattedPointDescription,
204+
descriptionFormatter: (point) => getPointAccessibleDescription(point, labels),
210205
...options.accessibility?.point,
211206
},
212207
},
@@ -312,7 +307,7 @@ export function InternalCoreChart({
312307
}}
313308
navigator={navigator}
314309
legend={
315-
settings.legendEnabled ? (
310+
context.legendEnabled ? (
316311
<ChartLegend {...legendOptions} position={legendPosition} api={api} i18nStrings={i18nStrings} />
317312
) : null
318313
}
@@ -333,7 +328,7 @@ export function InternalCoreChart({
333328
}
334329
/>
335330

336-
{settings.tooltipEnabled && (
331+
{context.tooltipEnabled && (
337332
<ChartTooltip
338333
{...tooltipOptions}
339334
i18nStrings={i18nStrings}
@@ -342,7 +337,7 @@ export function InternalCoreChart({
342337
/>
343338
)}
344339

345-
{settings.noDataEnabled && <ChartNoData {...noDataOptions} i18nStrings={i18nStrings} api={api} />}
340+
{context.noDataEnabled && <ChartNoData {...noDataOptions} i18nStrings={i18nStrings} api={api} />}
346341
</div>
347342
);
348343
}

0 commit comments

Comments
 (0)