Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix legends selection bugs",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,10 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
super(props);
this._createSet = memoizeFunction((data: IChartProps) => this._createDataSet(data.lineChartData!));
this.state = {
selectedLegend: '',
activeLegend: '',
hoverXValue: '',
isCalloutVisible: false,
isLegendSelected: false,
isLegendHovered: false,
refSelected: null,
YValueHover: [],
lineXValue: 0,
Expand Down Expand Up @@ -430,42 +429,28 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
this._chart = this._drawGraph(containerHeight, xAxis, yAxis, xElement!);
};

private _onLegendClick(customMessage: string): void {
if (this.state.isLegendSelected) {
if (this.state.activeLegend === customMessage) {
this.setState({
isLegendSelected: false,
activeLegend: '',
});
} else {
this.setState({
activeLegend: customMessage,
});
}
private _onLegendClick(legend: string): void {
if (this.state.selectedLegend === legend) {
this.setState({
selectedLegend: '',
});
} else {
this.setState({
activeLegend: customMessage,
selectedLegend: legend,
});
}
}

private _onLegendHover(customMessage: string): void {
if (this.state.isLegendSelected === false) {
this.setState({
activeLegend: customMessage,
isLegendHovered: true,
});
}
private _onLegendHover(legend: string): void {
this.setState({
activeLegend: legend,
});
}

private _onLegendLeave(isLegendFocused?: boolean): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to keep track of isLegendFocused flag.

Copy link
Contributor Author

@krkshitij krkshitij Oct 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not needed in the new logic

if (!!isLegendFocused || this.state.isLegendSelected === false) {
this.setState({
activeLegend: '',
isLegendHovered: false,
isLegendSelected: isLegendFocused ? false : this.state.isLegendSelected,
});
}
private _onLegendLeave(): void {
this.setState({
activeLegend: '',
});
}

private _getLegendData = (palette: IPalette, points: ILineChartPoints[]): JSX.Element => {
Expand Down Expand Up @@ -493,8 +478,8 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
hoverAction: () => {
this._onLegendHover(singleChartData.legend);
},
onMouseOutAction: (isLegendSelected?: boolean) => {
this._onLegendLeave(isLegendSelected);
onMouseOutAction: () => {
this._onLegendLeave();
},
};

Expand All @@ -518,14 +503,11 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
this.setState({ isCircleClicked: true });
};

private _getOpacity = (selectedArea: string): number => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is _getOpacity applicable for legends

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is only applicable for elements inside the chart

private _getOpacity = (legend: string): number => {
if (!this._isMultiStackChart) {
return 0.7;
} else {
let opacity = 0.7;
if (this.state.isLegendHovered || this.state.isLegendSelected) {
opacity = this.state.activeLegend === selectedArea ? 0.7 : 0.1;
}
const opacity = this._legendHighlighted(legend) || this._noLegendHighlighted() ? 0.7 : 0.1;
return opacity;
}
};
Expand All @@ -538,8 +520,8 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
if (this.state.isCalloutVisible) {
opacity = 1;
}
if (this.state.isLegendHovered || this.state.isLegendSelected) {
opacity = this.state.activeLegend === legend ? 0 : 0.1;
if (!this._noLegendHighlighted()) {
opacity = this._legendHighlighted(legend) ? 0 : 0.1;
}
return opacity;
}
Expand Down Expand Up @@ -701,4 +683,23 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
isCalloutVisible: false,
});
};

/**
* This function checks if the given legend is highlighted or not.
* A legend can be highlighted in 2 ways:
* 1. selection: if the user clicks on it
* 2. hovering: if there is no selected legend and the user hovers over it
*/
private _legendHighlighted = (legend: string) => {
return (
this.state.selectedLegend === legend || (this.state.selectedLegend === '' && this.state.activeLegend === legend)
);
};

/**
* This function checks if none of the legends is selected or hovered.
*/
private _noLegendHighlighted = () => {
return this.state.selectedLegend === '' && this.state.activeLegend === '';
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export interface IDonutChartState {
_height?: number | undefined;
activeLegend?: string;
color?: string | undefined;
isLegendSelected?: boolean;
xCalloutValue?: string;
yCalloutValue?: string;
focusedArcId?: string;
Expand Down Expand Up @@ -61,10 +60,9 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
_height: this.props.height || 200,
activeLegend: '',
color: '',
isLegendSelected: false,
xCalloutValue: '',
yCalloutValue: '',
selectedLegend: 'none',
selectedLegend: '',
focusedArcId: '',
};
this._hoverCallback = this._hoverCallback.bind(this);
Expand Down Expand Up @@ -124,7 +122,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
hoverLeaveCallback={this._hoverLeave}
uniqText={this._uniqText}
onBlurCallback={this._onBlur}
activeArc={this.state.activeLegend}
activeArc={this._getHighlightedLegend()}
focusedArcId={this.state.focusedArcId || ''}
href={this.props.href!}
calloutId={this._calloutId}
Expand Down Expand Up @@ -196,28 +194,17 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
title: point.legend!,
color: color,
action: () => {
if (this.state.isLegendSelected) {
if (this.state.activeLegend !== point.legend || this.state.activeLegend === '') {
this.setState({ activeLegend: point.legend!, isLegendSelected: true });
} else {
this.setState({ activeLegend: point.legend });
}
if (this.state.selectedLegend === point.legend) {
this.setState({ selectedLegend: '' });
} else {
this.setState({ activeLegend: point.legend!, isLegendSelected: true });
this.setState({ selectedLegend: point.legend! });
}
},
hoverAction: () => {
if (this.state.activeLegend !== point.legend || this.state.activeLegend === '') {
this.setState({ activeLegend: point.legend! });
} else {
this.setState({ activeLegend: point.legend });
}
this.setState({ activeLegend: point.legend! });
},
onMouseOutAction: () => {
this.setState({
showHover: false,
activeLegend: '',
});
this.setState({ activeLegend: '' });
},
};
return legend;
Expand All @@ -229,7 +216,6 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
overflowProps={this.props.legendsOverflowProps}
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard}
overflowText={this.props.legendsOverflowText}
selectedLegend={this.state.selectedLegend}
{...this.props.legendProps}
/>
);
Expand All @@ -240,7 +226,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
this._currentHoverElement = element;
this.setState({
/** Show the callout if highlighted arc is focused and Hide it if unhighlighted arc is focused */
showHover: this.state.activeLegend === data.legend || this.state.activeLegend === '',
showHover: this.state.selectedLegend === '' || this.state.selectedLegend === data.legend,
value: data.data!.toString(),
legend: data.legend,
color: data.color!,
Expand All @@ -258,7 +244,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
this._currentHoverElement = e;
this.setState({
/** Show the callout if highlighted arc is hovered and Hide it if unhighlighted arc is hovered */
showHover: this.state.activeLegend === data.legend || this.state.activeLegend === '',
showHover: this.state.selectedLegend === '' || this.state.selectedLegend === data.legend,
value: data.data!.toString(),
legend: data.legend,
color: data.color!,
Expand All @@ -283,10 +269,11 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
};

private _valueInsideDonut(valueInsideDonut: string | number | undefined, data: IChartDataPoint[]) {
if (valueInsideDonut !== undefined && this.state.activeLegend !== '' && !this.state.showHover) {
const highlightedLegend = this._getHighlightedLegend();
if (valueInsideDonut !== undefined && highlightedLegend !== '' && !this.state.showHover) {
let legendValue = valueInsideDonut;
data!.map((point: IChartDataPoint, index: number) => {
if (point.legend === this.state.activeLegend) {
if (point.legend === highlightedLegend) {
legendValue = point.yAxisCalloutData ? point.yAxisCalloutData : point.data!;
}
return;
Expand All @@ -304,4 +291,14 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar
}
return localeString?.toString();
}

/**
* This function returns
* the selected legend if there is one
* or the hovered legend if none of the legends is selected.
* Note: This won't work in case of multiple legends selection.
*/
private _getHighlightedLegend() {
return this.state.selectedLegend || this.state.activeLegend;
}
}
Loading