Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Focus indicator bug in bar charts #28414

Merged
merged 54 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e645d0e
focus indicator bug fix in bar chart
singlayush Jun 28, 2023
543b005
added bars spacing adjustment factor
singlayush Jun 28, 2023
ab9e8fa
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Jun 28, 2023
b31bd7c
resolving eslint issues
singlayush Jun 28, 2023
a612f24
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Jun 28, 2023
9fbe4f3
updated snapshots, for horizontal bar chart and multistacked bar chart
singlayush Jun 29, 2023
afb9f64
pixel design issue fixed
singlayush Jun 29, 2023
050e36f
Merge branch 'master' into focus-bug
yush-singla Jun 29, 2023
ef4a855
snapshot updated
singlayush Jun 29, 2023
376a20e
Merge branch 'focus-bug' of https://github.com/yush-singla/fluentui i…
singlayush Jun 29, 2023
7763568
added change, and fixed stroke thickness in horizontal bar chart
singlayush Jun 29, 2023
0a829fc
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Jul 3, 2023
b3c4c41
focus bug fixed
singlayush Jul 3, 2023
c4b7b45
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 3, 2023
26446c7
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 4, 2023
bcbb342
Merge branch 'focus-indicator-bug' of https://github.com/yush-singla/…
singlayush Jul 12, 2023
bcfcf20
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Jul 12, 2023
ed3601f
added support for dark mode, fixed few PR comments
singlayush Jul 12, 2023
b9b3974
updated snapshots
singlayush Jul 12, 2023
d85e8f1
removed dark mode
singlayush Jul 12, 2023
a11a5c7
fixed linting error
singlayush Jul 13, 2023
bf7002e
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 13, 2023
227dc13
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 13, 2023
4ab2417
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 14, 2023
197227c
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 24, 2023
e1abfec
Merge branch 'master' into focus-indicator-bug
yush-singla Jul 25, 2023
ce1f924
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Jul 29, 2023
8418368
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
Jul 29, 2023
b8d4d42
bar chart fixes
Jul 31, 2023
c40c4a0
fixed the focus indicator width in horizontal bar chart
Jul 31, 2023
40f8ec4
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
Jul 31, 2023
e4cf583
updated snapshots
Jul 31, 2023
8765fe0
resolved merge conflict
Aug 11, 2023
5370a18
Merge branch 'focus-indicator-bug' of https://github.com/yush-singla/…
singlayush Aug 16, 2023
03707a9
resolved merge conflict
singlayush Aug 16, 2023
6ee0e2d
resolved snapshots
singlayush Aug 16, 2023
9104dd5
fixed the focus indicator bug with different mathematical approach
singlayush Aug 17, 2023
c6f1750
removed emptychart flag
singlayush Aug 17, 2023
4809be8
Merge branch 'master' into focus-indicator-bug
yush-singla Aug 17, 2023
115fa30
Merge branch 'master' into focus-indicator-bug
yush-singla Aug 18, 2023
0f5704a
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Aug 18, 2023
d0dff41
Merge branch 'focus-indicator-bug' of https://github.com/yush-singla/…
singlayush Aug 18, 2023
747730e
stacked bar chart focus indicator bug resolved
singlayush Aug 18, 2023
661bfab
Merge branch 'master' into focus-indicator-bug
yush-singla Aug 18, 2023
5c1ef75
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Aug 19, 2023
87e1676
stacked bar chart bug fix
singlayush Aug 19, 2023
8027679
Merge branch 'focus-indicator-bug' of https://github.com/yush-singla/…
singlayush Aug 19, 2023
a36afa7
Merge branch 'master' into focus-indicator-bug
yush-singla Aug 21, 2023
eb43aca
Merge branch 'master' into focus-indicator-bug
yush-singla Aug 22, 2023
a90a3c8
merged
singlayush Aug 23, 2023
cae22df
Merge branch 'master' of https://github.com/microsoft/fluentui into f…
singlayush Aug 24, 2023
f6c95fc
fixed the outline bug in dark mode
singlayush Aug 24, 2023
6d35a89
Merge branch 'focus-indicator-bug' of https://github.com/yush-singla/…
singlayush Aug 24, 2023
b638967
multistaked bar chart variable initialisation
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 the focus indicator bug in horizontal bar chart and multi stack bar chart",
"packageName": "@fluentui/react-charting",
"email": "yushsingla@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IHorizontalBarChartState {
yCalloutValue?: string;
barCalloutProps?: IChartDataPoint;
callOutAccessibilityData?: IAccessibilityProps;
barSpacingInPercent: number;
}

export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartProps, IHorizontalBarChartState> {
Expand All @@ -40,6 +41,7 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
private _refArray: IRefArrayData[];
private _calloutAnchorPoint: IChartDataPoint | null;
private _isRTL: boolean = getRTL();
private barChartSvgRef: React.RefObject<SVGSVGElement>;
private _emptyChartId: string;

constructor(props: IHorizontalBarChartProps) {
Expand All @@ -54,12 +56,23 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
color: '',
xCalloutValue: '',
yCalloutValue: '',
barSpacingInPercent: 0,
};
this._refArray = [];
this._uniqLineText = '_HorizontalLine_' + Math.random().toString(36).substring(7);
this._hoverOff = this._hoverOff.bind(this);
this._calloutId = getId('callout');
this._emptyChartId = getId('_HBC_empty');
this.barChartSvgRef = React.createRef<SVGSVGElement>();
}

public componentDidMount(): void {
const svgWidth = this.barChartSvgRef.current?.getBoundingClientRect().width || 0;
const MARGIN_WIDTH_IN_PX = 2;
if (svgWidth) {
const currentBarSpacing = (MARGIN_WIDTH_IN_PX / svgWidth) * 100;
this.setState({ barSpacingInPercent: currentBarSpacing });
}
}

public render(): JSX.Element {
Expand Down Expand Up @@ -113,7 +126,7 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
</FocusZone>
{points!.chartData![0].data && this._createBenchmark(points!)}
<FocusZone direction={FocusZoneDirection.horizontal} className={this._classNames.chartWrapper}>
<svg className={this._classNames.chart} aria-label={points!.chartTitle}>
<svg ref={this.barChartSvgRef} className={this._classNames.chart} aria-label={points!.chartTitle}>
<g
id={keyVal}
key={keyVal}
Expand Down Expand Up @@ -286,7 +299,18 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
);
}

/**
* This functions returns an array of <rect> elements, which form the bars
* For each bar an x value, and a width needs to be specified
* The computations are done based on percentages
* Extra margin is also provided, in the x value to provide some spacing in between the bars
*/

private _createBars(data: IChartProps, palette: IPalette): JSX.Element[] {
const noOfBars =
data.chartData?.reduce((count: number, point: IChartDataPoint) => (count += (point.data || 0) > 0 ? 1 : 0), 0) ||
1;
const totalMarginPercent = this.state.barSpacingInPercent * (noOfBars - 1);
const defaultPalette: string[] = [palette.blueLight, palette.blue, palette.blueMid, palette.red, palette.black];
// calculating starting point of each bar and it's range
const startingPoint: number[] = [];
Expand All @@ -312,7 +336,15 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
return sumOfPercent;
});

const scalingRatio = sumOfPercent !== 0 ? sumOfPercent / 100 : 1;
/**
* The %age of the space occupied by the margin needs to subtracted
* while computing the scaling ratio, since the margins are not being
* scaled down, only the data is being scaled down from a higher percentage to lower percentage
* Eg: 95% of the space is taken by the bars, 5% by the margins
* Now if the sumOfPercent is 120% -> This needs to be scaled down to 95%, not 100%
* since that's only space available to the bars
*/
const scalingRatio = sumOfPercent !== 0 ? (sumOfPercent - totalMarginPercent) / 100 : 1;

const bars = data.chartData!.map((point: IChartDataPoint, index: number) => {
const color: string = point.color ? point.color : defaultPalette[Math.floor(Math.random() * 4 + 1)];
Expand Down Expand Up @@ -359,7 +391,11 @@ export class HorizontalBarChartBase extends React.Component<IHorizontalBarChartP
return (
<rect
key={index}
x={`${this._isRTL ? 100 - startingPoint[index] - value : startingPoint[index]}%`}
x={`${
this._isRTL
? 100 - startingPoint[index] - value - index * this.state.barSpacingInPercent
: startingPoint[index] + index * this.state.barSpacingInPercent
}%`}
y={0}
data-is-focusable={point.legend !== '' ? true : false}
width={value + '%'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ export const getHorizontalBarChartStyles = (props: IHorizontalBarChartStyleProps
display: 'block',
overflow: 'visible',
},
barWrapper: {
stroke: theme.palette.white,
strokeWidth: 2,
},
barWrapper: {},
chartTitle: {
...theme.fonts.small,
display: 'flex',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,7 @@ exports[`HorizontalBarChart - mouse events Should render callout correctly on mo
aria-label="2020/04/30, 94%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#004b50"
height={12}
Expand All @@ -158,10 +155,7 @@ exports[`HorizontalBarChart - mouse events Should render callout correctly on mo
aria-label="13457/15000."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={false}
fill="#edebe9"
height={12}
Expand Down Expand Up @@ -304,10 +298,7 @@ exports[`HorizontalBarChart - mouse events Should render callout correctly on mo
aria-label="2020/04/30, 19%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#5c2d91"
height={12}
Expand All @@ -325,10 +316,7 @@ exports[`HorizontalBarChart - mouse events Should render callout correctly on mo
aria-label="14200/15000."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={false}
fill="#edebe9"
height={12}
Expand Down Expand Up @@ -647,10 +635,7 @@ exports[`HorizontalBarChart - mouse events Should render customized callout on m
aria-label="2020/04/30, 94%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#004b50"
height={12}
Expand All @@ -668,10 +653,7 @@ exports[`HorizontalBarChart - mouse events Should render customized callout on m
aria-label="13457/15000."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={false}
fill="#edebe9"
height={12}
Expand Down Expand Up @@ -814,10 +796,7 @@ exports[`HorizontalBarChart - mouse events Should render customized callout on m
aria-label="2020/04/30, 19%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#5c2d91"
height={12}
Expand All @@ -835,10 +814,7 @@ exports[`HorizontalBarChart - mouse events Should render customized callout on m
aria-label="14200/15000."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={false}
fill="#edebe9"
height={12}
Expand Down Expand Up @@ -1063,10 +1039,7 @@ exports[`HorizontalBarChart snapShot testing Should not render bar labels in abs
aria-label="2020/04/30, 94%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#004b50"
height={12}
Expand Down Expand Up @@ -1195,10 +1168,7 @@ exports[`HorizontalBarChart snapShot testing Should not render bar labels in abs
aria-label="2020/04/30, 19%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#5c2d91"
height={12}
Expand Down Expand Up @@ -1344,10 +1314,7 @@ exports[`HorizontalBarChart snapShot testing Should render absolute-scale varian
aria-label="2020/04/30, 94%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#004b50"
height={12}
Expand Down Expand Up @@ -1494,10 +1461,7 @@ exports[`HorizontalBarChart snapShot testing Should render absolute-scale varian
aria-label="2020/04/30, 19%."
className=

{
stroke-width: 2px;
stroke: #ffffff;
}

data-is-focusable={true}
fill="#5c2d91"
height={12}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface IMultiStackedBarChartState {
dataPointCalloutProps?: IChartDataPoint;
callOutAccessibilityData?: IAccessibilityProps;
calloutLegend: string;
barSpacingInPercent: number;
}

export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarChartProps, IMultiStackedBarChartState> {
Expand All @@ -52,6 +53,7 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
private _calloutAnchorPoint: IChartDataPoint | null;
private _longestBarTotalValue: number;
private _isRTL: boolean = getRTL();
private barChartSvgRef: React.RefObject<SVGSVGElement>;
private _emptyChartId: string;
private _barId: string;
private _barIdPlaceholderPartToWhole: string;
Expand All @@ -70,16 +72,27 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
xCalloutValue: '',
yCalloutValue: '',
calloutLegend: '',
barSpacingInPercent: 0,
};
this._onLeave = this._onLeave.bind(this);
this._onBarLeave = this._onBarLeave.bind(this);
this._calloutId = getId('callout');
this.barChartSvgRef = React.createRef<SVGSVGElement>();
this._emptyChartId = getId('_MSBC_empty');
this._barId = getId('_MSBC_rect_');
this._barIdPlaceholderPartToWhole = getId('_MSBC_rect_partToWhole_');
this._barIdEmpty = getId('_MSBC_rect_empty');
}

public componentDidMount(): void {
const svgWidth = this.barChartSvgRef.current?.getBoundingClientRect().width || 0;
const MARGIN_WIDTH_IN_PX = 2;
if (svgWidth) {
const currentBarSpacing = (MARGIN_WIDTH_IN_PX / svgWidth) * 100;
this.setState({ barSpacingInPercent: currentBarSpacing });
}
}

public render(): JSX.Element {
if (!this._isChartEmpty()) {
const { data, theme, culture } = this.props;
Expand Down Expand Up @@ -155,6 +168,13 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
);
}

/**
* The Create bar functions returns an array of <rect> elements, which form the bars
* For each bar an x value, and a width needs to be specified
* The computations are done based on percentages
* Extra margin is also provided, in the x value to provide some spacing
*/

private _createBarsAndLegends(
data: IChartProps,
barHeight: number,
Expand All @@ -163,6 +183,10 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
hideDenominator: boolean,
href?: string,
): JSX.Element {
const noOfBars =
data.chartData?.reduce((count: number, point: IChartDataPoint) => (count += (point.data || 0) > 0 ? 1 : 0), 0) ||
1;
const totalMarginPercent = this.state.barSpacingInPercent * (noOfBars - 1);
const { culture } = this.props;
const defaultPalette: string[] = [palette.blueLight, palette.blue, palette.blueMid, palette.red, palette.black];
// calculating starting point of each bar and it's range
Expand All @@ -177,7 +201,9 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
let sumOfPercent = 0;
data.chartData!.map((point: IChartDataPoint, index: number) => {
const pointData = point.data ? point.data : 0;
value = (pointData / total) * 100 ? (pointData / total) * 100 : 0;
const currValue = (pointData / total) * 100;
value = currValue ? currValue : 0;

if (value < 1 && value !== 0) {
value = 1;
} else if (value > 99 && value !== 100) {
Expand All @@ -201,7 +227,16 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
sumOfPercent += value;
}

const scalingRatio = sumOfPercent !== 0 ? sumOfPercent / 100 : 1;
/**
* The %age of the space occupied by the margin needs to subtracted
* while computing the scaling ratio, since the margins are not being
* scaled down, only the data is being scaled down from a higher percentage to lower percentage
* Eg: 95% of the space is taken by the bars, 5% by the margins
* Now if the sumOfPercent is 120% -> This needs to be scaled down to 95%, not 100%
* since that's only space available to the bars
*/

const scalingRatio = sumOfPercent !== 0 ? sumOfPercent / (100 - totalMarginPercent) : 1;

let prevPosition = 0;
let value = 0;
Expand Down Expand Up @@ -257,7 +292,11 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
<rect
key={index}
id={`${this._barId}-${index}`}
x={`${this._isRTL ? 100 - startingPoint[index] - value : startingPoint[index]}%`}
x={`${
this._isRTL
? 100 - startingPoint[index] - value - this.state.barSpacingInPercent * index
: startingPoint[index] + this.state.barSpacingInPercent * index
}%`}
y={0}
width={value + '%'}
height={barHeight}
Expand All @@ -273,9 +312,10 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
key="text"
x={`${
this._isRTL
? 100 - (startingPoint[startingPoint.length - 1] || 0) - value
: (startingPoint[startingPoint.length - 1] || 0) + value
? 100 - (startingPoint[startingPoint.length - 1] || 0) - value - totalMarginPercent
: (startingPoint[startingPoint.length - 1] || 0) + value + totalMarginPercent
}%`}
textAnchor={this._isRTL ? 'end' : 'start'}
y={barHeight / 2}
dominantBaseline="central"
transform={`translate(${this._isRTL ? -4 : 4})`}
Expand Down Expand Up @@ -362,7 +402,7 @@ export class MultiStackedBarChartBase extends React.Component<IMultiStackedBarCh
</FocusZone>
<FocusZone direction={FocusZoneDirection.horizontal}>
<div className={this._classNames.chartWrapper}>
<svg className={this._classNames.chart} aria-label={data?.chartTitle}>
<svg ref={this.barChartSvgRef} className={this._classNames.chart} aria-label={data?.chartTitle}>
{bars}
</svg>
</div>
Expand Down
Loading