diff --git a/change/@fluentui-react-charts-35c73142-960e-4539-8688-379264b9cc34.json b/change/@fluentui-react-charts-35c73142-960e-4539-8688-379264b9cc34.json new file mode 100644 index 00000000000000..3560648a1e6e25 --- /dev/null +++ b/change/@fluentui-react-charts-35c73142-960e-4539-8688-379264b9cc34.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Accessibility bug fixes", + "packageName": "@fluentui/react-charts", + "email": "132879294+v-baambati@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.test.tsx b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.test.tsx index ad4edf64915154..02cfe8232670b9 100644 --- a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.test.tsx +++ b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.test.tsx @@ -399,7 +399,7 @@ describe('Grouped vertical bar chart - Subcomponent bar', () => { { data: chartPoints, barWidth: 'auto', maxBarWidth: 50 }, container => { // Assert - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); expect(bars).toHaveLength(6); expect(bars[0].getAttribute('width')).toEqual('50'); expect(bars[1].getAttribute('width')).toEqual('50'); @@ -414,7 +414,7 @@ describe('Grouped vertical bar chart - Subcomponent bar', () => { container => { // colors mentioned in the data points itself // Assert - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); expect(bars[0].getAttribute('fill')).toEqual('#00bcf2'); expect(bars[1].getAttribute('fill')).toEqual('#0078d4'); expect(bars[2].getAttribute('fill')).toEqual('#00bcf2'); @@ -440,7 +440,7 @@ describe('Grouped vertical bar chart - Subcomponent bar', () => { { data: accessibilityDataPoints }, container => { // Assert - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); expect(bars[0]).toHaveAttribute( 'aria-label', 'Group series 1 of 4, Bar series 1 of 2 x-Axis 2020/04/30 MetaData1 33%', @@ -473,7 +473,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { //const legends = getByClass(container, /legend-/i); const legends = screen.getAllByText((content, element) => element!.tagName.toLowerCase() === 'button'); fireEvent.mouseOver(legends[0]); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); // Assert expect(bars[0]).toHaveAttribute('opacity', ''); expect(bars[1]).toHaveAttribute('opacity', '0.1'); @@ -491,7 +491,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { container => { const legends = screen.getAllByText((content, element) => element!.tagName.toLowerCase() === 'button'); fireEvent.mouseOver(legends[0]); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); // Assert expect(bars[0]).toHaveAttribute('opacity', ''); expect(bars[1]).toHaveAttribute('opacity', '0.1'); @@ -500,7 +500,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { expect(bars[4]).toHaveAttribute('opacity', ''); expect(bars[5]).toHaveAttribute('opacity', '0.1'); fireEvent.mouseOver(legends[1]); - const updatedBars = container.querySelectorAll('rect[role="img"]'); + const updatedBars = container.querySelectorAll('rect[role="option"]'); // Assert expect(updatedBars[0]).toHaveAttribute('opacity', '0.1'); expect(updatedBars[1]).toHaveAttribute('opacity', ''); @@ -521,7 +521,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { const legendsAfterClickEvent = screen.getAllByText( (content, element) => element!.tagName.toLowerCase() === 'button', ); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); // Assert expect(legendsAfterClickEvent[0]).toHaveAttribute('aria-selected', 'true'); expect(legendsAfterClickEvent[1]).toHaveAttribute('aria-selected', 'false'); @@ -545,7 +545,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { const legendsAfterClickEvent = screen.getAllByText( (content, element) => element!.tagName.toLowerCase() === 'button', ); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); // Assert expect(legendsAfterClickEvent[0]).toHaveAttribute('aria-selected', 'false'); expect(legendsAfterClickEvent[1]).toHaveAttribute('aria-selected', 'false'); @@ -576,7 +576,7 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => { expect(firstLegend).toHaveAttribute('aria-selected', 'true'); expect(secondLegend).toHaveAttribute('aria-selected', 'true'); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); expect(bars[0]).toHaveAttribute('opacity', '0.1'); expect(bars[1]).toHaveAttribute('opacity', ''); expect(bars[2]).toHaveAttribute('opacity', ''); @@ -729,7 +729,7 @@ describe('GroupedVerticalBarChart - mouse events', () => { it('Should render callout correctly on mouseover', async () => { const { container } = render(); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); fireEvent.mouseOver(bars[0]); // Wait for any async updates (if needed) act(() => { @@ -740,7 +740,7 @@ describe('GroupedVerticalBarChart - mouse events', () => { it('Should render callout correctly on mousemove', () => { const { container } = render(); - const bars = container.querySelectorAll('rect[role="img"]'); + const bars = container.querySelectorAll('rect[role="option"]'); fireEvent.mouseMove(bars[2]); const html1 = container.innerHTML; fireEvent.mouseMove(bars[3]); diff --git a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.tsx b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.tsx index 4a620d58b06659..4565be3c935dd3 100644 --- a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.tsx +++ b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.tsx @@ -594,7 +594,7 @@ export const GroupedVerticalBarChart: React.FC = R onClick={pointData.onClick} aria-label={getAriaLabel(pointData, singleSet.xAxisPoint)} tabIndex={_legendHighlighted(pointData.legend) || _noLegendHighlighted() ? 0 : undefined} - role="img" + role="option" />, ); @@ -636,8 +636,14 @@ export const GroupedVerticalBarChart: React.FC = R } } }); + const legendNames = presentLegends.join(', '); + const categoryGroupAriaLabel = `${singleSet.xAxisPoint}, category ${singleSet.indexNum + 1} of ${ + _datasetForBars.length + }, with bar series: ${legendNames}.`; return ( @@ -871,7 +877,7 @@ export const GroupedVerticalBarChart: React.FC = R tabIndex={shouldHighlight ? 0 : undefined} onFocus={e => _onLineFocus(e, series, seriesIdx, pointIdx)} onBlur={_onBarLeave} - role="img" + role="listbox" aria-label={getAriaLabel( { xAxisCalloutData: point.xAxisCalloutData, diff --git a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChart.test.tsx.snap b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChart.test.tsx.snap index 21b439b03667c7..486d0d71aaddbe 100644 --- a/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChart.test.tsx.snap +++ b/packages/charts/react-charts/library/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChart.test.tsx.snap @@ -209,6 +209,8 @@ exports[`Grouped Vertical bar chart rendering Should render the grouped vertical /> _hoverOn(event, xValue, point) : undefined } onFocus={_showToolTipOnSegment && point.legend !== '' ? event => _hoverOn(event, xValue, point) : undefined} - role="img" - aria-label={_getAriaLabel(point)} + role={index !== placeholderIndex ? 'option' : ''} + aria-label={index !== placeholderIndex ? _getAriaLabel(point) : undefined} onBlur={_hoverOff} onMouseLeave={_hoverOff} className={classes.barWrapper} opacity={isLegendSelected ? 1 : 0.1} - tabIndex={_legendHighlighted(point.legend!) || _noLegendHighlighted() ? 0 : undefined} + tabIndex={ + index !== placeholderIndex && (_legendHighlighted(point.legend!) || _noLegendHighlighted()) ? 0 : undefined + } /> ); }); @@ -417,6 +419,7 @@ export const HorizontalBarChart: React.FunctionComponent { _refCallback(e, points!.chartData![0].legend); }} diff --git a/packages/charts/react-charts/library/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChart.test.tsx.snap b/packages/charts/react-charts/library/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChart.test.tsx.snap index b8ed93962b5c34..decd6bd0782edd 100644 --- a/packages/charts/react-charts/library/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChart.test.tsx.snap +++ b/packages/charts/react-charts/library/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChart.test.tsx.snap @@ -39,7 +39,9 @@ exports[`Horizontal bar chart - Screen resolution Should remain unchanged on zoo class="fui-hbc__chart" > 0. if (xRange[0] < xRange[1]) { - allBars = stackedChartData - .map(singleBarData => + allBars = stackedChartData.map((singleBarData, groupIndex) => { + const groupBars = _yAxisType === YAxisType.NumericAxis ? _createNumericBars( containerHeight, @@ -204,9 +204,18 @@ export const HorizontalBarChartWithAxis: React.FunctionComponent + {groupBars} + + ); + }); } return (_bars = allBars); @@ -471,7 +480,7 @@ export const HorizontalBarChartWithAxis: React.FunctionComponent) => _onBarHover(point, startColor, event)} aria-label={_getAriaLabel(point)} - role="img" + role="option" aria-labelledby={`toolTip${_calloutId}`} onMouseLeave={_onBarLeave} onFocus={event => _onBarFocus(event, point, index, startColor)} @@ -646,7 +655,7 @@ export const HorizontalBarChartWithAxis: React.FunctionComponent { _refCallback(e, point.legend!); }} @@ -771,6 +780,11 @@ export const HorizontalBarChartWithAxis: React.FunctionComponent - - - - + - 40k - - - + - 25k - - - + - 20k - + + + @@ -856,110 +876,130 @@ exports[`Horizontal bar chart with axis rendering Should render the Horizontal b - - - - + - 5k - - - + - 3k - - - + - 2k - + + + @@ -1337,106 +1377,126 @@ Object { - - - - - - - - + + + + + + + + + + + + + + + + @@ -1864,140 +1924,160 @@ Object { - - - - - - - - - - - -
-
-
-
@@ -2977,106 +3077,126 @@ Object { - - - - + - 40k - - - + - 25k - - - + - 20k - + + +
@@ -3563,106 +3683,126 @@ Object { - - - - - - - - + + + + + + + + + + + + + + + +
@@ -4090,166 +4230,186 @@ Object {
- - - - - - - - - - - -
-
-
- - -
+
+
+
+ + +
-
-
-
-
+
+
+
+ - -
+
+
+
+ + +
-
-
- , - "container":
+ + + + + + + + + + + + + + +
+
+
+ , + "container":
- - - - + + + + + + + + + + + +
@@ -6842,110 +7072,130 @@ Object { - - - - - - - - + + + + + + + + + + + + + + + +
@@ -7343,110 +7593,130 @@ Object { - - - - + - 5k - - - + - 3k - - - + - 2k - + + +
@@ -7879,110 +8149,130 @@ Object { - - - - - - - - + + + + + + + + + + + + + + + +
@@ -8351,110 +8641,130 @@ Object { - - - - + - 5k - - - + - 3k - - - + - 2k - + + +
diff --git a/packages/charts/react-charts/library/src/components/VegaDeclarativeChart/__snapshots__/VegaDeclarativeChart.test.tsx.snap b/packages/charts/react-charts/library/src/components/VegaDeclarativeChart/__snapshots__/VegaDeclarativeChart.test.tsx.snap index cd8af79f37cdb7..6214416573489b 100644 --- a/packages/charts/react-charts/library/src/components/VegaDeclarativeChart/__snapshots__/VegaDeclarativeChart.test.tsx.snap +++ b/packages/charts/react-charts/library/src/components/VegaDeclarativeChart/__snapshots__/VegaDeclarativeChart.test.tsx.snap @@ -240,7 +240,9 @@ exports[`VegaDeclarativeChart - Bar+Line Combo Rendering Bar + Line Combinations > - - - - - - - - - - - + + + + + + + + + + + + +
@@ -819,7 +830,9 @@ exports[`VegaDeclarativeChart - Bar+Line Combo Rendering Bar + Line Combinations > - - - - - + + + + + + +
@@ -1262,7 +1284,9 @@ exports[`VegaDeclarativeChart - Bar+Line Combo Rendering Bar + Line Combinations > - - - - - + + + + + + +
@@ -1782,7 +1815,9 @@ exports[`VegaDeclarativeChart - Bar+Line Combo Rendering Bar + Line Combinations > - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + + + diff --git a/packages/charts/react-charts/library/src/components/VerticalBarChart/VerticalBarChart.tsx b/packages/charts/react-charts/library/src/components/VerticalBarChart/VerticalBarChart.tsx index 5db23b01126325..755d60e929d4a5 100644 --- a/packages/charts/react-charts/library/src/components/VerticalBarChart/VerticalBarChart.tsx +++ b/packages/charts/react-charts/library/src/components/VerticalBarChart/VerticalBarChart.tsx @@ -262,10 +262,10 @@ export const VerticalBarChart: React.FunctionComponent = ); return ( - <> + {line} {dots} - + ); } @@ -661,7 +661,7 @@ export const VerticalBarChart: React.FunctionComponent = yBarScale(yReferencePoint); const baselineHeight = containerHeight - margins.bottom! - yBarScale(yReferencePoint); return ( - + = onClick={point.onClick} onMouseOver={event => _onBarHover(point, colorScale(point.y), event)} aria-label={_getAriaLabel(point)} - role="img" + role="option" onMouseLeave={_onBarLeave} onFocus={event => _onBarFocus(event, point, index, colorScale(point.y))} onBlur={_onBarLeave} @@ -723,6 +723,8 @@ export const VerticalBarChart: React.FunctionComponent = return ( = width={_barWidth} height={adjustedBarHeight} aria-label={_getAriaLabel(point)} - role="img" + role="option" ref={(e: SVGRectElement) => { _refCallback(e, point.legend!); }} @@ -782,7 +784,11 @@ export const VerticalBarChart: React.FunctionComponent = yBarScale(yReferencePoint); const baselineHeight = containerHeight - margins.bottom! - yBarScale(yReferencePoint); return ( - + = onClick={point.onClick} onMouseOver={event => _onBarHover(point, colorScale(point.y), event)} aria-label={_getAriaLabel(point)} - role="img" + role="option" onMouseLeave={_onBarLeave} onFocus={event => _onBarFocus(event, point, index, colorScale(point.y))} onBlur={_onBarLeave} @@ -1099,6 +1105,13 @@ export const VerticalBarChart: React.FunctionComponent = return categoryToValues; } + function _getBarsGroupLabel(): string { + // Calculate number of unique series and total data points for accessibility label + const uniqueSeries = new Set(_points.map(point => point.legend)).size; + const totalDataPoints = _points.length; + return `${uniqueSeries} series and ${totalDataPoints} data points`; + } + function updatePosition(newX: number, newY: number) { const threshold = 1; // Set a threshold for movement const { x, y } = clickPosition; @@ -1180,17 +1193,14 @@ export const VerticalBarChart: React.FunctionComponent = return ( <> {_bars} - {_isHavingLine && ( - - {_createLine( - props.xScale!, - props.yScalePrimary!, - props.containerHeight, - props.containerWidth, - props.yScaleSecondary, - )} - - )} + {_isHavingLine && + _createLine( + props.xScale!, + props.yScalePrimary!, + props.containerHeight, + props.containerWidth, + props.yScaleSecondary, + )} ); }} diff --git a/packages/charts/react-charts/library/src/components/VerticalBarChart/__snapshots__/VerticalBarChart.test.tsx.snap b/packages/charts/react-charts/library/src/components/VerticalBarChart/__snapshots__/VerticalBarChart.test.tsx.snap index 40c152a54aef51..d39c687f7ac35d 100644 --- a/packages/charts/react-charts/library/src/components/VerticalBarChart/__snapshots__/VerticalBarChart.test.tsx.snap +++ b/packages/charts/react-charts/library/src/components/VerticalBarChart/__snapshots__/VerticalBarChart.test.tsx.snap @@ -411,14 +411,17 @@ exports[`Screen resolution Should remain unchanged on zoom in 1`] = ` - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + { + const lines: React.ReactNode[] = []; + const borderForLines: React.ReactNode[] = []; + const dots: React.ReactNode[] = []; const shouldHighlight = _isLegendHighlighted(item) || _noLegendHighlighted(); for (let i = 1; i < lineObject[item].length; i++) { const x1 = xScale(lineObject[item][i - 1].xItem.xAxisPoint); @@ -623,8 +624,6 @@ export const VerticalStackedBarChart: React.FunctionComponent, ); } - }); - Object.keys(lineObject).forEach((item: string, index: number) => { lineObject[item].forEach((circlePoint: LinePoint, subIndex: number) => { const circleRef: { refElement: SVGCircleElement | null } = { refElement: null }; const yScaleBandwidthTranslate = @@ -658,19 +657,25 @@ export const VerticalStackedBarChart: React.FunctionComponent _lineFocus(event, circlePoint, circleRef)} onBlur={_handleMouseOut} - role="img" + role="option" aria-label={_getAriaLabel(circlePoint.xItem, circlePoint as VSChartDataPoint, true)} />, ); }); + + lineSeriesGroups.push( + + {borderForLines} + {lines} + {dots} + , + ); }); - return ( - <> - {borderForLines} - {lines} - {dots} - - ); + return <>{lineSeriesGroups}; } function _getCircleOpacityAndRadius( @@ -1042,7 +1047,7 @@ export const VerticalStackedBarChart: React.FunctionComponent _onRectFocus(point, singleChartData.xAxisPoint as string, startColor, ref), onBlur: _handleMouseOut, onClick: (event: React.MouseEvent) => _onClick(point, event), - role: 'img', + role: 'option', tabIndex: !props.hideTooltip && shouldHighlight ? 0 : undefined, }; @@ -1145,7 +1150,6 @@ export const VerticalStackedBarChart: React.FunctionComponent _onStackFocus(singleChartData, groupRef), onBlur: _handleMouseOut, onClick: (event: any) => _onClick(singleChartData, event), - role: 'img', tabIndex: !props.hideTooltip ? 0 : undefined, }; let showLabel = false; @@ -1183,6 +1187,8 @@ export const VerticalStackedBarChart: React.FunctionComponent { groupRef.refElement = e; }} + role="listbox" + aria-label={_getAriaLabel(singleChartData)} {...stackFocusProps} > {singleBar} @@ -1199,7 +1205,7 @@ export const VerticalStackedBarChart: React.FunctionComponent @@ -1337,7 +1343,8 @@ export const VerticalStackedBarChart: React.FunctionComponent - - - - - + + + + + + + @@ -747,7 +758,9 @@ exports[`Vertical stacked bar chart - Screen resolution Should remain unchanged > - - - - - + + + + + + + @@ -1440,7 +1462,9 @@ exports[`Vertical stacked bar chart rendering Should render the vertical stacked > - - - + + + + + @@ -3695,7 +3738,9 @@ exports[`VerticalStackedBarChart snapShot testing Should not render bar labels 1 >