Skip to content

Commit

Permalink
[GVBC] Enabling multiple legend selection for Grouped Vertical Bar Ch…
Browse files Browse the repository at this point in the history
…art (#33511)
  • Loading branch information
srmukher authored Dec 26, 2024
1 parent 6a70a11 commit 37e5f0b
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Enabling multiple legend selection for Grouped Vertical Bar Chart",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface IGroupedVerticalBarChartState extends IBasestate {
dataPointCalloutProps?: IGVBarChartSeriesPoint;
callOutAccessibilityData?: IAccessibilityProps;
calloutLegend: string;
selectedLegends: string[];
}

export class GroupedVerticalBarChartBase
Expand Down Expand Up @@ -121,7 +122,7 @@ export class GroupedVerticalBarChartBase
dataForHoverCard: 0,
isCalloutVisible: false,
refSelected: null,
selectedLegend: props.legendProps?.selectedLegend ?? '',
selectedLegends: props.legendProps?.selectedLegends || [],
xCalloutValue: '',
yCalloutValue: '',
YValueHover: [],
Expand Down Expand Up @@ -333,7 +334,7 @@ export class GroupedVerticalBarChartBase
this.setState({
refSelected: mouseEvent,
/** Show the callout if highlighted bar is hovered and Hide it if unhighlighted bar is hovered */
isCalloutVisible: this.state.selectedLegend === '' || this.state.selectedLegend === pointData.legend,
isCalloutVisible: this._noLegendHighlighted() || this._legendHighlighted(pointData.legend),
calloutLegend: pointData.legend,
dataForHoverCard: pointData.data,
color: pointData.color,
Expand Down Expand Up @@ -369,7 +370,7 @@ export class GroupedVerticalBarChartBase
this.setState({
refSelected: obj.refElement,
/** Show the callout if highlighted bar is focused and Hide it if unhighlighted bar is focused */
isCalloutVisible: this.state.selectedLegend === '' || this.state.selectedLegend === pointData.legend,
isCalloutVisible: this._noLegendHighlighted() || this._legendHighlighted(pointData.legend),
calloutLegend: pointData.legend,
dataForHoverCard: pointData.data,
color: pointData.color,
Expand Down Expand Up @@ -581,18 +582,6 @@ export class GroupedVerticalBarChartBase
});
};

private _onLegendClick(legendTitle: string): void {
if (this.state.selectedLegend === legendTitle) {
this.setState({
selectedLegend: '',
});
} else {
this.setState({
selectedLegend: legendTitle,
});
}
}

private _onLegendHover(legendTitle: string): void {
this.setState({
activeLegend: legendTitle,
Expand Down Expand Up @@ -624,9 +613,6 @@ export class GroupedVerticalBarChartBase
const legend: ILegend = {
title: point.legend,
color,
action: () => {
this._onLegendClick(point.legend);
},
hoverAction: () => {
this._handleChartMouseLeave();
this._onLegendHover(point.legend);
Expand All @@ -646,10 +632,26 @@ export class GroupedVerticalBarChartBase
enabledWrapLines={this.props.enabledLegendsWrapLines}
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard}
{...this.props.legendProps}
onChange={this._onLegendSelectionChange.bind(this)}
/>
);
};

private _onLegendSelectionChange(
selectedLegends: string[],
event: React.MouseEvent<HTMLButtonElement>,
currentLegend?: ILegend,
): void {
if (this.props.legendProps?.canSelectMultipleLegends) {
this.setState({ selectedLegends });
} else {
this.setState({ selectedLegends: selectedLegends.slice(-1) });
}
if (this.props.legendProps?.onChange) {
this.props.legendProps.onChange(selectedLegends, event, currentLegend);
}
}

private _getAxisData = (yAxisData: IAxisData) => {
if (yAxisData && yAxisData.yAxisDomainValues.length) {
const { yAxisDomainValues: domainValue } = yAxisData;
Expand All @@ -664,19 +666,24 @@ export class GroupedVerticalBarChartBase
* 2. hovering: if there is no selected legend and the user hovers over it
*/
private _legendHighlighted = (legendTitle: string) => {
return (
this.state.selectedLegend === legendTitle ||
(this.state.selectedLegend === '' && this.state.activeLegend === legendTitle)
);
return this._getHighlightedLegend().includes(legendTitle!);
};

/**
* This function checks if none of the legends is selected or hovered.
*/
private _noLegendHighlighted = () => {
return this.state.selectedLegend === '' && this.state.activeLegend === '';
return this._getHighlightedLegend().length === 0;
};

private _getHighlightedLegend() {
return this.state.selectedLegends.length > 0
? this.state.selectedLegends
: this.state.activeLegend
? [this.state.activeLegend]
: [];
}

private _getAriaLabel = (point: IGVBarChartSeriesPoint, xAxisPoint: string): string => {
const xValue = point.xAxisCalloutData || xAxisPoint;
const legend = point.legend;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,206 @@ const chartPoints = [
},
];

const dataGVBC = [
{
name: 'Jan - Mar',
series: [
{
key: 'series1',
data: 33000,
color: DefaultPalette.blue,
legend: '2022',
xAxisCalloutData: '2022/04/30',
yAxisCalloutData: '29%',
callOutAccessibilityData: {
ariaLabel: 'Group Jan - Mar 1 of 4, Bar series 1 of 2 2022, x value 2022/04/30, y value 29%',
},
},
{
key: 'series2',
data: 44000,
color: DefaultPalette.green,
legend: '2023',
xAxisCalloutData: '2023/04/30',
yAxisCalloutData: '44%',
callOutAccessibilityData: {
ariaLabel: 'Group Jan - Mar 1 of 4, Bar series 2 of 2 2023, x value 2023/04/30, y value 44%',
},
},
{
key: 'series3',
data: 54000,
color: DefaultPalette.red,
legend: '2024',
xAxisCalloutData: '2024/04/30',
yAxisCalloutData: '44%',
callOutAccessibilityData: {
ariaLabel: 'Group Jan - Mar 1 of 4, Bar series 3 of 4 2022, x value 2024/04/30, y value 44%',
},
},
{
key: 'series4',
data: 24000,
color: DefaultPalette.yellow,
legend: '2021',
xAxisCalloutData: '2021/04/30',
yAxisCalloutData: '44%',
callOutAccessibilityData: {
ariaLabel: 'Group Jan - Mar 1 of 4, Bar series 4 of 4 2021, x value 2021/04/30, y value 44%',
},
},
],
},
{
name: 'Apr - Jun',
series: [
{
key: 'series1',
data: 33000,
color: DefaultPalette.blue,
legend: '2022',
xAxisCalloutData: '2022/05/30',
yAxisCalloutData: '29%',
callOutAccessibilityData: {
ariaLabel: 'Group Apr - Jun 2 of 4, Bar series 1 of 2 2022, x value 2022/05/30, y value 29%',
},
},
{
key: 'series2',
data: 3000,
color: DefaultPalette.green,
legend: '2023',
xAxisCalloutData: '2023/05/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Apr - Jun 2 of 4, Bar series 2 of 2 2023, x value 2023/05/30, y value 3%',
},
},
{
key: 'series3',
data: 9000,
color: DefaultPalette.red,
legend: '2024',
xAxisCalloutData: '2024/05/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Apr - Jun 2 of 4, Bar series 3 of 4 2024, x value 2024/05/30, y value 3%',
},
},
{
key: 'series4',
data: 12000,
color: DefaultPalette.yellow,
legend: '2021',
xAxisCalloutData: '2021/05/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Apr - Jun 2 of 4, Bar series 4 of 4 2021, x value 2021/05/30, y value 3%',
},
},
],
},

{
name: 'Jul - Sep',
series: [
{
key: 'series1',
data: 14000,
color: DefaultPalette.blue,
legend: '2022',
xAxisCalloutData: '2022/06/30',
yAxisCalloutData: '13%',
callOutAccessibilityData: {
ariaLabel: 'Group Jul - Sep 3 of 4, Bar series 1 of 2 2022, x value 2022/06/30, y value 13%',
},
},
{
key: 'series2',
data: 50000,
color: DefaultPalette.green,
legend: '2023',
xAxisCalloutData: '2023/06/30',
yAxisCalloutData: '50%',
callOutAccessibilityData: {
ariaLabel: 'Group Jul - Sep 3 of 4, Bar series 2 of 2 2023, x value 2023/06/30, y value 50%',
},
},
{
key: 'series3',
data: 60000,
color: DefaultPalette.red,
legend: '2024',
xAxisCalloutData: '2024/06/30',
yAxisCalloutData: '50%',
callOutAccessibilityData: {
ariaLabel: 'Group Jul - Sep 3 of 4, Bar series 3 of 4 2024, x value 2024/06/30, y value 50%',
},
},
{
key: 'series4',
data: 10000,
color: DefaultPalette.yellow,
legend: '2021',
xAxisCalloutData: '2021/06/30',
yAxisCalloutData: '50%',
callOutAccessibilityData: {
ariaLabel: 'Group Jul - Sep 3 of 4, Bar series 4 of 4 2021, x value 2021/06/30, y value 50%',
},
},
],
},
{
name: 'Oct - Dec',
series: [
{
key: 'series1',
data: 33000,
color: DefaultPalette.blue,
legend: '2022',
xAxisCalloutData: '2022/07/30',
yAxisCalloutData: '29%',
callOutAccessibilityData: {
ariaLabel: 'Group Oct - Dec 4 of 4, Bar series 1 of 2 2022, x value 2022/07/30, y value 29%',
},
},
{
key: 'series2',
data: 3000,
color: DefaultPalette.green,
legend: '2023',
xAxisCalloutData: '2023/07/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Oct - Dec 4 of 4, Bar series 2 of 2 2023, x value 2023/07/30, y value 3%',
},
},
{
key: 'series3',
data: 6000,
color: DefaultPalette.red,
legend: '2024',
xAxisCalloutData: '2024/07/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Oct - Dec 4 of 4, Bar series 3 of 4 2024, x value 2024/07/30, y value 3%',
},
},
{
key: 'series4',
data: 15000,
color: DefaultPalette.yellow,
legend: '2021',
xAxisCalloutData: '2021/07/30',
yAxisCalloutData: '3%',
callOutAccessibilityData: {
ariaLabel: 'Group Oct - Dec 4 of 4, Bar series 4 of 4 2021, x value 2021/07/30, y value 3%',
},
},
],
},
];

describe('Grouped Vertical bar chart rendering', () => {
beforeEach(updateChartWidthAndHeight);
afterEach(sharedAfterEach);
Expand Down Expand Up @@ -350,6 +550,40 @@ describe('Grouped vertical bar chart - Subcomponent Legends', () => {
expect(bars[5]).toHaveAttribute('opacity', '');
},
);

testWithoutWait(
'Should select multiple legends on click',
GroupedVerticalBarChart,
{ data: dataGVBC, legendProps: { canSelectMultipleLegends: true } },
container => {
const firstLegend = screen.queryByText('2023')?.closest('button');
const secondLegend = screen.queryByText('2024')?.closest('button');

expect(firstLegend).toBeDefined();
expect(secondLegend).toBeDefined();

fireEvent.click(firstLegend!);
fireEvent.click(secondLegend!);

// Assert
expect(firstLegend).toHaveAttribute('aria-selected', 'true');
expect(secondLegend).toHaveAttribute('aria-selected', 'true');

const bars = screen.getAllByText((content, element) => element!.tagName.toLowerCase() === 'rect');
expect(bars[0]).toHaveAttribute('opacity', '0.1');
expect(bars[1]).toHaveAttribute('opacity', '');
expect(bars[2]).toHaveAttribute('opacity', '');
expect(bars[3]).toHaveAttribute('opacity', '0.1');
expect(bars[4]).toHaveAttribute('opacity', '0.1');
expect(bars[5]).toHaveAttribute('opacity', '');
expect(bars[6]).toHaveAttribute('opacity', '');
expect(bars[7]).toHaveAttribute('opacity', '0.1');
expect(bars[8]).toHaveAttribute('opacity', '0.1');
expect(bars[9]).toHaveAttribute('opacity', '');
expect(bars[10]).toHaveAttribute('opacity', '');
expect(bars[11]).toHaveAttribute('opacity', '0.1');
},
);
});

describe('Grouped vertical bar chart - Subcomponent callout', () => {
Expand Down
Loading

0 comments on commit 37e5f0b

Please sign in to comment.