Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
4fb4fbd
changes for pie chart accessibiilty issues
singlayush Jul 6, 2023
f089da0
pie chart accessibility
singlayush Jul 7, 2023
cf47e6e
pie chart accessibility issue fixed, also example width adjustment added
singlayush Jul 7, 2023
8d31ab2
examples made width and height adjustable
singlayush Jul 7, 2023
3e797b0
fixed svg tooltip text
singlayush Jul 10, 2023
2760098
added role to multistacked bar chart
singlayush Jul 11, 2023
9c369ca
pie chart build fixes and snapshot updates
singlayush Jul 11, 2023
2e02515
screen reader bug fixes
singlayush Jul 11, 2023
827a430
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Jul 12, 2023
9d8d81e
pie chart POC
singlayush Jul 12, 2023
b321e75
pie chart spacing added betweeen the pie
singlayush Jul 12, 2023
81b1c28
snapshots updated
singlayush Jul 12, 2023
a5a1221
removed unused imports
singlayush Jul 13, 2023
2a9f6ab
Merge branch 'master' into pie-chart-focus-spacing
yush-singla Jul 13, 2023
515bad4
examples fixed
singlayush Jul 13, 2023
5281f8f
Merge branch 'pie-chart-focus-spacing' of https://github.com/yush-sin…
singlayush Jul 13, 2023
96441c1
Merge branch 'master' into pie-chart-focus-spacing
yush-singla Jul 13, 2023
8fde75a
fixed changes post review
singlayush Jul 14, 2023
595d65b
Merge branch 'pie-chart-focus-spacing' of https://github.com/yush-sin…
singlayush Jul 14, 2023
7661478
upddated snaphots
singlayush Jul 14, 2023
a027380
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Jul 14, 2023
75f836e
lint fix
singlayush Jul 14, 2023
b76477b
lint fix
singlayush Jul 14, 2023
9fbea37
fixed pie chart color behaviour
singlayush Jul 14, 2023
c8b0a8f
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Jul 24, 2023
177e567
fixed PR issues
singlayush Jul 24, 2023
9fe14c6
snapshot updates
singlayush Jul 24, 2023
6ad3845
Merge branch 'master' into pie-chart-focus-spacing
yush-singla Jul 25, 2023
8de0c12
testing with adding the component to the application
singlayush Jul 25, 2023
5de7a77
Merge branch 'vr-tests-experimentation' into pie-chart-focus-spacing
singlayush Jul 25, 2023
ef75286
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Jul 25, 2023
a59ff6c
removed vr test
singlayush Jul 25, 2023
5a9054d
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Jul 28, 2023
c0a16e0
added pie chart fixes
singlayush Jul 28, 2023
cdca5ab
Merge branch 'pie-chart-focus-spacing' of https://github.com/yush-sin…
singlayush Jul 28, 2023
1403a05
pr issue resolution
singlayush Jul 28, 2023
7330024
updated snapshots
singlayush Jul 28, 2023
46c6e8f
fixed eslint error
singlayush Jul 28, 2023
317816f
Merge branch 'pie-chart-focus-spacing' of https://github.com/yush-sin…
Aug 2, 2023
b461958
fixed dark theme bug in pie chart
Aug 10, 2023
668304c
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
Aug 10, 2023
2095b11
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
Aug 11, 2023
30767f9
added mock function to snapshot test for pie chart
Aug 11, 2023
31ceac3
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Aug 16, 2023
e4fbf26
fixing pr comments
singlayush Aug 16, 2023
6fdafce
fixed RTL bug
singlayush Aug 16, 2023
1ae4489
updated snapshots
singlayush Aug 16, 2023
f22e1af
Merge branch 'master' into pie-chart-focus-spacing
yush-singla Aug 18, 2023
2bee446
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Aug 18, 2023
9d7c9df
Merge branch 'master' of https://github.com/microsoft/fluentui into p…
singlayush Aug 24, 2023
04f4886
added classname arc
singlayush Aug 24, 2023
140d2bc
Merge branch 'pie-chart-focus-spacing' of https://github.com/yush-sin…
singlayush Aug 24, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fixed accessibility issues in pie chart",
"packageName": "@fluentui/react-charting",
"email": "yushsingla@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { IArcProps, IArcStyles } from './Arc.types';
import { DefaultPalette } from '@fluentui/react/lib/Styling';
export const getStyles = (props: IArcProps): IArcStyles => {
import { DefaultPalette, ITheme } from '@fluentui/react/lib/Styling';
export const getStyles = (props: IArcProps, theme: ITheme | undefined): IArcStyles => {
const { color } = props;
return {
root: { fill: color, stroke: DefaultPalette.white, strokeWidth: 2 },
arcRoot: { fill: color },
arcRootFocussed: { fill: color, stroke: theme?.palette.black || DefaultPalette.black, strokeWidth: 2 },
arc: { outline: 'none' },
arcText: { fill: theme?.palette.black || DefaultPalette.black, outline: 'none' },
};
};
96 changes: 81 additions & 15 deletions packages/react-charting/src/components/PieChart/Arc/Arc.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,112 @@
import * as React from 'react';
import * as shape from 'd3-shape';
import { IArcProps, IArcStyles } from './Arc.types';
import { classNamesFunction } from '@fluentui/react/lib/Utilities';
import { IArcProps, IArcState, IArcStyles } from './Arc.types';
import { classNamesFunction, getId, getRTL } from '@fluentui/react/lib/Utilities';
import { getStyles } from './Arc.styles';
import { convertToLocaleString } from '../../../utilities/utilities';
import { wrapContent, convertToLocaleString } from '../../../utilities/utilities';
import { SVGTooltipText } from '../../../utilities/SVGTooltipText';

export class Arc extends React.Component<IArcProps, {}> {
export class Arc extends React.Component<IArcProps, IArcState> {
public static defaultProps: Partial<IArcProps> = {
arc: shape.arc(),
};

public state: {} = {};
protected _arcId: string;

public static getDerivedStateFromProps(nextProps: Readonly<IArcProps>): null {
_updateChart(nextProps);
return null;
}

public constructor(props: IArcProps) {
super(props);
this.state = {
isArcFocused: false,
};

this._arcId = getId('piechart_arc');
}

public updateChart = (newProps: IArcProps) => {
_updateChart(newProps);
};

public render(): JSX.Element {
const { color, arc } = this.props;
const { arc } = this.props;
const getClassNames = classNamesFunction<IArcProps, IArcStyles>();
const classNames = getClassNames(getStyles, { color });
return <path d={arc(this.props.data)} className={classNames.root} onClick={this.props.data?.data.onClick} />;
const classNames = getClassNames(props => getStyles(props, this.props.theme), { ...this.props });

return (
<path
d={arc(this.props.data)}
className={`${this.state.isArcFocused ? classNames.arcRootFocussed : classNames.arcRoot}`}
onClick={this.props.data?.data.onClick}
/>
);
}

protected _onFocus = () => {
this.setState({ isArcFocused: true });
};

protected _onBlur = () => {
this.setState({ isArcFocused: false });
};
}

export class LabeledArc extends Arc {
private _isRTL = getRTL();

public constructor(props: IArcProps) {
super(props);
this._arcId = getId('piechart_arc');
}

public render(): JSX.Element {
const { data, arc, culture } = this.props;
const [labelX, labelY] = arc.centroid(data);
const labelTranslate = `translate(${labelX}, ${labelY})`;
const { data, culture } = this.props;
const gap = 4;
// placing the labels on the outside arc
const [labelX, labelY] = shape.arc().centroid({
endAngle: data?.endAngle || 0,
startAngle: data?.startAngle || 0,
padAngle: data?.padAngle,
innerRadius: this.props?.outerRadius || 0,
outerRadius: this.props?.outerRadius || 0 + gap,
});

const getClassNames = classNamesFunction<IArcProps, IArcStyles>();
const classNames = getClassNames(props => getStyles(props, this.props.theme));

const angle = ((data?.startAngle || 0) + (data?.endAngle || 0)) / 2;

const content = `${data?.data.x}-${convertToLocaleString(data?.data.y, culture)}`;

return (
<g className="arc">
<g
className={`${classNames.arc} arc`}
data-is-focusable={true}
id={this._arcId}
onFocus={this._onFocus}
onBlur={this._onBlur}
aria-label={content}
role="img"
>
{super.render()}
<text transform={labelTranslate} textAnchor="middle">
{data!.data!.x}-{convertToLocaleString(data!.data!.y, culture)}
</text>
<SVGTooltipText
content={content}
textProps={{
x: labelX,
y: labelY,
dominantBaseline: angle > Math.PI / 2 && angle < (3 * Math.PI) / 2 ? 'hanging' : 'auto',
textAnchor: (!this._isRTL && angle > Math.PI) || (this._isRTL && angle < Math.PI) ? 'end' : 'start',
'aria-label': `${data?.data.x}-${convertToLocaleString(data?.data.y, culture)}`,
className: classNames.arcText,
}}
isTooltipVisibleProp={this.state.isArcFocused}
shouldReceiveFocus={false}
maxWidth={40}
wrapContent={wrapContent}
/>
</g>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IStyle } from '@fluentui/react/lib/Styling';
import { IStyle, ITheme } from '@fluentui/react/lib/Styling';
import { IDataPoint } from '../PieChart.types';

export interface IArcProps {
Expand Down Expand Up @@ -30,6 +30,17 @@ export interface IArcProps {
* The prop used to define the culture to localized the numbers
*/
culture?: string;
/**
* to pass the theme
*/
theme?: ITheme;
}

export interface IArcState {
/**
* The state controls, whether the arc needs to be focused or not
*/
isArcFocused?: boolean;
}

export interface IArcData {
Expand Down Expand Up @@ -63,5 +74,11 @@ export interface IArcStyles {
/**
* Style set for the card header component root
*/
root: IStyle;
arcRoot: IStyle;

arc: IStyle;

arcRootFocussed: IStyle;

arcText: IStyle;
}
26 changes: 21 additions & 5 deletions packages/react-charting/src/components/PieChart/Pie/Pie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import * as scale from 'd3-scale';
import { IPieProps } from './Pie.types';
import { LabeledArc } from '../Arc/Arc';
import { IArcData } from '../Arc/Arc.types';
import { FocusZone, FocusZoneDirection } from '@fluentui/react-focus';
import { getColorFromToken, getNextColor } from '../../../utilities/colors';

export class Pie extends React.Component<IPieProps, {}> {
public static defaultProps: Partial<IPieProps> = {
pie: shape
.pie()
.padAngle(0.01)
.sort(null)
/* eslint-disable @typescript-eslint/no-explicit-any */
.value((d: any) => d.y),
Expand All @@ -28,23 +31,36 @@ export class Pie extends React.Component<IPieProps, {}> {
innerRadius={this.props.innerRadius}
outerRadius={this.props.outerRadius}
color={`${this.colors(i)}`}
theme={this.props.theme}
/>
);
};

public render(): JSX.Element {
// const getClassNames = classNamesFunction<IPieProps, IPieStyles>();
const { pie, colors, data, width, height, chartTitle } = this.props;
const { pie, data, width, height, chartTitle, theme } = this.props;

this.colors = scale.scaleOrdinal().range(colors!);
const defaultColors: Array<string> = [];
if (data && !this.props.colors) {
for (let i = 0; i < data.length; i++) {
defaultColors.push(getNextColor(i, 0, theme?.isInverted));
}
}
const { colors = defaultColors } = this.props;

this.colors = scale.scaleOrdinal().range(colors.map(color => getColorFromToken(color)));

const piechart = pie(data);
const translate = `translate(${width / 2}, ${height / 2})`;

return (
<svg width={width} height={height} aria-label={chartTitle}>
<g transform={translate}>{piechart.map((d: IArcData, i: number) => this.arcGenerator(d, i))}</g>
</svg>
<FocusZone direction={FocusZoneDirection.domOrder}>
<svg width={width} height={height} aria-label={chartTitle}>
<g className="pie" transform={translate}>
{piechart.map((d: IArcData, i: number) => this.arcGenerator(d, i))}
</g>
</svg>
</FocusZone>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IStyle } from '@fluentui/react/lib/Styling';
import { IStyle, ITheme } from '@fluentui/react/lib/Styling';
import { IDataPoint } from '../PieChart.types';

export interface IPieProps {
Expand Down Expand Up @@ -40,6 +40,8 @@ export interface IPieProps {
* The prop used to define the culture to localized the numbers
*/
culture?: string;

theme?: ITheme;
}

export interface IPieStyles {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,17 @@ export class PieChartBase extends React.Component<IPieChartProps, {}> {
height: height!,
className,
});
const radius = Math.min(width!, height!) / 2;
const outerRadius = radius - 10;

const TEXT_MAX_WIDTH = 40;
const TEXT_LINE_HEIGHT = 16;

/**
* The radius for the pie chart is computed based on the space available inside the svg
* after subtracting the max amount of space that can be used by the text in pie chart
*/

const radius = Math.min(width! - 2 * TEXT_MAX_WIDTH, height! - 2 * TEXT_LINE_HEIGHT) / 2;
const outerRadius = radius;

return !this._isChartEmpty() ? (
<div className={this._classNames.root}>
Expand All @@ -41,10 +50,11 @@ export class PieChartBase extends React.Component<IPieChartProps, {}> {
width={width!}
height={height!}
outerRadius={outerRadius}
innerRadius={0}
innerRadius={1}
data={data!}
colors={colors!}
chartTitle={chartTitle!}
theme={theme}
/>
</div>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { queryAllByAttribute, render, waitFor } from '@testing-library/react';
import { PieChart } from './index';
import { chartPoints, colors } from './PieChart.test';
import * as utils from '../../utilities/utilities';

describe('Pie chart rendering', () => {
test('Should re-render the Pie chart with data', async () => {
Expand All @@ -11,6 +12,11 @@ describe('Pie chart rendering', () => {
// Assert
expect(container).toMatchSnapshot();
expect(getById(container, /_PieChart_empty/i)).toHaveLength(1);

// Mock the implementation of wrapContent as it internally calls a Browser Function like
// getComputedTextLength() which will otherwise lead to a crash if mounted
jest.spyOn(utils, 'wrapContent').mockImplementation(() => false);

// Act
rerender(<PieChart data={chartPoints} colors={colors} />);
await waitFor(() => {
Expand Down
Loading