-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[Area Chart] Support legend multi selection #33475
base: master
Are you sure you want to change the base?
Changes from all commits
936c244
3691842
950cbe2
73f6397
b5419e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,6 +194,7 @@ export interface IAccessibilityProps { | |
|
||
// @public | ||
export interface IAreaChartProps extends ICartesianChartProps { | ||
canSelectMultipleLegends?: boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
culture?: string; | ||
data: IChartProps; | ||
enableGradient?: boolean; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,6 +80,7 @@ export interface IAreaChartState extends IBasestate { | |
isShowCalloutPending: boolean; | ||
/** focused point */ | ||
activePoint: string; | ||
selectedLegends: string[]; | ||
} | ||
|
||
export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartState> { | ||
|
@@ -124,8 +125,8 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
super(props); | ||
this._createSet = memoizeFunction(this._createDataSet); | ||
this.state = { | ||
selectedLegend: props.legendProps?.selectedLegend ?? '', | ||
activeLegend: '', | ||
selectedLegends: [], | ||
activeLegend: undefined, | ||
hoverXValue: '', | ||
isCalloutVisible: false, | ||
refSelected: null, | ||
|
@@ -287,6 +288,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
const { data } = this.props; | ||
const { lineChartData } = data; | ||
// This will get the value of the X when mouse is on the chart | ||
const { selectedLegends } = this.state; | ||
const xOffset = this._xAxisRectScale.invert(pointer(mouseEvent)[0], document.getElementById(this._rectId)!); | ||
const i = bisect(lineChartData![0].data, xOffset); | ||
const d0 = lineChartData![0].data[i - 1] as ILineChartDataPoint; | ||
|
@@ -338,16 +340,20 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
const pointToHighlightUpdated = this.state.nearestCircleToHighlight !== nearestCircleToHighlight; | ||
// if no points need to be called out then don't show vertical line and callout card | ||
if (found && pointToHighlightUpdated && !this.state.isShowCalloutPending) { | ||
const filteredValues = | ||
selectedLegends.length > 0 | ||
? found.values.filter((value: { legend: string }) => selectedLegends.includes(value.legend)) | ||
: found.values; | ||
this.setState({ | ||
nearestCircleToHighlight, | ||
isCalloutVisible: false, | ||
isShowCalloutPending: true, | ||
lineXValue: this._xAxisRectScale(pointToHighlight), | ||
displayOfLine: InterceptVisibility.show, | ||
isCircleClicked: false, | ||
stackCalloutProps: found!, | ||
YValueHover: found.values, | ||
dataPointCalloutProps: found!, | ||
stackCalloutProps: { ...found, values: filteredValues }, | ||
YValueHover: filteredValues, | ||
dataPointCalloutProps: { ...found, values: filteredValues }, | ||
hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate, | ||
xAxisCalloutAccessibilityData, | ||
activePoint: '', | ||
|
@@ -560,18 +566,6 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
this._chart = this._drawGraph(containerHeight, xAxis, yAxis, xElement!); | ||
}; | ||
|
||
private _onLegendClick(legend: string): void { | ||
if (this.state.selectedLegend === legend) { | ||
this.setState({ | ||
selectedLegend: '', | ||
}); | ||
} else { | ||
this.setState({ | ||
selectedLegend: legend, | ||
}); | ||
} | ||
} | ||
|
||
private _onLegendHover(legend: string): void { | ||
this.setState({ | ||
activeLegend: legend, | ||
|
@@ -580,7 +574,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
|
||
private _onLegendLeave(): void { | ||
this.setState({ | ||
activeLegend: '', | ||
activeLegend: undefined, | ||
}); | ||
} | ||
|
||
|
@@ -600,9 +594,6 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
const legend: ILegend = { | ||
title: singleChartData.legend, | ||
color, | ||
action: () => { | ||
this._onLegendClick(singleChartData.legend); | ||
}, | ||
hoverAction: () => { | ||
this._handleChartMouseLeave(); | ||
this._onLegendHover(singleChartData.legend); | ||
|
@@ -621,10 +612,16 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
enabledWrapLines={this.props.enabledLegendsWrapLines} | ||
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard} | ||
{...this.props.legendProps} | ||
canSelectMultipleLegends={this.props.canSelectMultipleLegends} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
onChange={this._onLegendChange} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
/> | ||
); | ||
}; | ||
|
||
private _onLegendChange = (selectedLegends: string[]) => { | ||
this.setState({ selectedLegends }); | ||
}; | ||
|
||
private _onDataPointClick = (func: (() => void) | undefined) => { | ||
if (func) { | ||
func(); | ||
|
@@ -776,6 +773,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`; | ||
const xDataPoint = singlePoint.xVal instanceof Date ? singlePoint.xVal.getTime() : singlePoint.xVal; | ||
lineColor = points[index]!.color!; | ||
const legend = points[index]!.legend; | ||
return ( | ||
<circle | ||
key={circleId} | ||
|
@@ -792,7 +790,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
onFocus={() => this._handleFocus(index, pointIndex, circleId)} | ||
onBlur={this._handleBlur} | ||
{...getSecureProps(pointOptions)} | ||
r={this._getCircleRadius(xDataPoint, circleRadius, circleId)} | ||
r={this._getCircleRadius(xDataPoint, circleRadius, circleId, legend)} | ||
role="img" | ||
aria-label={this._getAriaLabel(index, pointIndex)} | ||
/> | ||
|
@@ -807,6 +805,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
if (this.state.nearestCircleToHighlight === xDataPoint) { | ||
const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`; | ||
lineColor = points[index]!.color!; | ||
const legend = points[index]!.legend; | ||
graph.push( | ||
<circle | ||
key={circleId} | ||
|
@@ -820,7 +819,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
onMouseOver={this._onRectMouseMove} | ||
onClick={this._onDataPointClick.bind(this, points[index]!.data[pointIndex].onDataPointClick!)} | ||
{...getSecureProps(pointOptions)} | ||
r={this._getCircleRadius(xDataPoint, circleRadius, circleId)} | ||
r={this._getCircleRadius(xDataPoint, circleRadius, circleId, legend)} | ||
/>, | ||
); | ||
} | ||
|
@@ -870,8 +869,14 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
return graph; | ||
}; | ||
|
||
private _getCircleRadius = (xDataPoint: number, circleRadius: number, circleId: string): number => { | ||
const { isCircleClicked, nearestCircleToHighlight, activePoint } = this.state; | ||
private _getCircleRadius = (xDataPoint: number, circleRadius: number, circleId: string, legend: string): number => { | ||
const { isCircleClicked, nearestCircleToHighlight, activePoint, selectedLegends } = this.state; | ||
|
||
// Show the circle if no legends are selected or if the point's legend is in the selected legends | ||
if (selectedLegends.length > 0 && !selectedLegends.includes(legend)) { | ||
return 0; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is missing out on legend hovered condition. |
||
|
||
if (isCircleClicked && nearestCircleToHighlight === xDataPoint) { | ||
return 1; | ||
} else if (nearestCircleToHighlight === xDataPoint || activePoint === circleId) { | ||
|
@@ -895,15 +900,16 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
*/ | ||
private _legendHighlighted = (legend: string) => { | ||
return ( | ||
this.state.selectedLegend === legend || (this.state.selectedLegend === '' && this.state.activeLegend === legend) | ||
this.state.selectedLegends.includes(legend) || | ||
(this.state.selectedLegends.length === 0 && 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 === ''; | ||
return this.state.selectedLegends.length === 0 && this.state.activeLegend === undefined; | ||
}; | ||
|
||
private _addDefaultColors = (lineChartData?: ILineChartPoints[]): ILineChartPoints[] => { | ||
|
@@ -930,14 +936,18 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt | |
const found: any = this._calloutPoints.find((e: { x: string | number }) => e.x === modifiedXVal); | ||
// Show details in the callout for the focused point only | ||
found.values = found.values.filter((e: { y: number }) => e.y === y); | ||
const filteredValues = | ||
this.state.selectedLegends.length > 0 | ||
? found.values.filter((value: { legend: string }) => this.state.selectedLegends.includes(value.legend)) | ||
: found.values; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move logic to function |
||
|
||
this.setState({ | ||
refSelected: `#${circleId}`, | ||
isCalloutVisible: true, | ||
hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate, | ||
YValueHover: found.values, | ||
stackCalloutProps: found, | ||
dataPointCalloutProps: found, | ||
YValueHover: filteredValues, | ||
stackCalloutProps: { ...found, values: filteredValues }, | ||
dataPointCalloutProps: { ...found, values: filteredValues }, | ||
activePoint: circleId, | ||
}); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕵🏾♀️ visual regressions to review in the fluentuiv8 Visual Regression Report
Callout 4 screenshots
react-charting-LineChart 1 screenshots