-
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
[Donut] Legends multi selection for Donut Charts #33447
base: master
Are you sure you want to change the base?
Conversation
📊 Bundle size report✅ No changes found |
Pull request demo site: URL |
roundCorners: boolean; | ||
} | ||
|
||
export class DonutChartMultipleExample extends React.Component<IDonutChartProps, IDonutChartState> { |
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.
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.
done
const opacity: number = | ||
this.props.activeArc === this.props.data!.data.legend || this.props.activeArc === '' ? 1 : 0.1; | ||
|
||
const isActive = activeArc?.includes(this.props.data?.data.legend!); |
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.
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.
so that it can be used to calculate both opacity and data-is-focusable without the need to add same checks again
@@ -354,7 +374,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
* Note: This won't work in case of multiple legends selection. | |||
*/ | |||
private _getHighlightedLegend() { | |||
return this.state.selectedLegend || this.state.activeLegend; |
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.
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.
For multiple select also checking if this.state.selectedLegends is populated other return the this.state.activeLegend
return this.state.selectedLegends.length > 0 ? this.state.selectedLegends : [this.state.activeLegend];
if (point.legend === highlightedLegend || (this.state.showHover && point.legend === this.state.legend)) { | ||
if ( | ||
highlightedLegends.includes(point.legend!) || | ||
(this.state.showHover && point.legend === this.state.legend) |
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.
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.
separated the logic
…icrosoft/fluentui into users/srmukher/LegendsMultiSel
@@ -265,11 +263,17 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard} | |||
overflowText={this.props.legendsOverflowText} | |||
{...this.props.legendProps} | |||
canSelectMultipleLegends={this.props.canSelectMultipleLegends} | |||
onChange={this._onLegendChange} |
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.
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.
modified as discussed offline
* Prop to enable the multiple legend selection | ||
* @default false | ||
*/ | ||
canSelectMultipleLegends?: boolean; |
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.
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.
done
this.props.activeArc === this.props.data!.data.legend || this.props.activeArc === '' ? 1 : 0.1; | ||
|
||
const isActive = activeArc?.includes(this.props.data?.data.legend!); | ||
const opacity: number = activeArc && activeArc.length > 0 ? (isActive ? 1 : 0.1) : 1; |
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.
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.
done
@@ -129,7 +131,8 @@ export class Arc extends React.Component<IArcProps, IArcState> { | |||
if ( | |||
hideLabels || | |||
Math.abs(data!.endAngle - data!.startAngle) < Math.PI / 12 || | |||
(activeArc !== data!.data.legend && activeArc !== '') | |||
!Array.isArray(activeArc) || | |||
(data!.data.legend && activeArc.includes(data!.data.legend)) |
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.
same logic is repeating
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.
moved to a function
@@ -354,7 +365,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
* Note: This won't work in case of multiple legends selection. | |||
*/ | |||
private _getHighlightedLegend() { |
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.
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.
this was already modified
@@ -488,6 +488,7 @@ export interface IDonutChart { | |||
// @public | |||
export interface IDonutChartProps extends ICartesianChartProps { | |||
calloutProps?: Partial<ICalloutProps>; | |||
canSelectMultipleLegends?: boolean; |
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.
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.
done
@@ -123,13 +124,17 @@ export class Arc extends React.Component<IArcProps, IArcState> { | |||
return point.callOutAccessibilityData?.ariaLabel || (legend ? `${legend}, ` : '') + `${yValue}.`; | |||
}; | |||
|
|||
private _shouldHighlightArc = (activeArc: string[], legend?: string): boolean => { |
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.
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.
_shouldHighlightArc has already been used in other places too, any other specific places ?
@@ -24,9 +24,10 @@ export interface IDonutChartState { | |||
xCalloutValue?: string; | |||
yCalloutValue?: string; | |||
focusedArcId?: string; | |||
selectedLegend: string; | |||
selectedLegend?: string; |
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.
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.
removed
@@ -138,7 +140,9 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
hoverLeaveCallback={this._hoverLeave} | |||
uniqText={this._uniqText} | |||
onBlurCallback={this._onBlur} | |||
activeArc={this._getHighlightedLegend()} | |||
activeArc={this._getHighlightedLegend().filter( |
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.
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.
done
}, | ||
hoverAction: () => { | ||
this._handleChartMouseLeave(); | ||
this.setState({ activeLegend: point.legend! }); | ||
}, | ||
onMouseOutAction: () => { | ||
this.setState({ activeLegend: '' }); | ||
this.setState({ activeLegend: undefined }); |
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.
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.
no, checked with a legend having '' title, it does not work in that case
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.
ok
}; | ||
|
||
private _isLegendHovered = (legend: string): boolean => { | ||
return this._noLegendsHighlighted() || this._isLegendHighlighted(legend); |
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.
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.
keeping this for now as we will take up the change in experience task later.
@@ -354,7 +387,7 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
* Note: This won't work in case of multiple legends selection. | |||
*/ | |||
private _getHighlightedLegend() { | |||
return this.state.selectedLegend || this.state.activeLegend; | |||
return this.state.selectedLegends.length > 0 ? this.state.selectedLegends : [this.state.activeLegend]; |
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.
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.
But, in line multiple chart, on multiple legend selection, legend hover is disabled. Shall the experience be not the same?
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.
Yes. We will need a different representation. Currently hover behavior is lost once one or more legend is selected. We will plan for it. You can continue with the current behavior for now.
d4339d8
to
6cbdf51
Compare
6cbdf51
to
be186ba
Compare
ca45bd3
to
943cff8
Compare
@@ -265,16 +259,41 @@ export class DonutChartBase extends React.Component<IDonutChartProps, IDonutChar | |||
focusZonePropsInHoverCard={this.props.focusZonePropsForLegendsInHoverCard} | |||
overflowText={this.props.legendsOverflowText} | |||
{...this.props.legendProps} | |||
// eslint-disable-next-line react/jsx-no-bind | |||
onChange={this._onLegendSelectionChange.bind(this)} |
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.
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.
This is used just once
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.
i mean the class instance "this"
if (this.props.legendProps?.canSelectMultipleLegends) { | ||
this.setState({ selectedLegends }); | ||
} else { | ||
this.setState({ selectedLegends: selectedLegends.slice(-1) }); |
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.
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.
yes working
|
||
private _noLegendsHighlighted = (): boolean => { | ||
return this._getHighlightedLegend().length === 0; | ||
}; |
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.
keep these functions together with getHighlightedLegends
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.
done
private _focusCallback = (data: IChartDataPoint, id: string, element: SVGPathElement): void => { | ||
this._currentHoverElement = element; | ||
this.setState({ | ||
/** Show the callout if highlighted arc is focused and Hide it if unhighlighted arc is focused */ | ||
showHover: this.state.selectedLegend === '' || this.state.selectedLegend === data.legend, | ||
showHover: this._noLegendsHighlighted() || this._isLegendHighlighted(data.legend!), |
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.
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.
done
const highlightedLegends = this._getHighlightedLegend().filter(legend => legend !== undefined); | ||
|
||
if (this.state.showHover) { | ||
const legendValue = data.find(point => point.legend === this.state.legend); |
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.
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.
calling it pointValue as point is used in data.find
} | ||
|
||
private _isLegendHighlighted = (legend: string): boolean => { | ||
return this._getHighlightedLegend().indexOf(legend) > -1; |
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.
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.
done
@@ -123,13 +124,16 @@ export class Arc extends React.Component<IArcProps, IArcState> { | |||
return point.callOutAccessibilityData?.ariaLabel || (legend ? `${legend}, ` : '') + `${yValue}.`; | |||
}; | |||
|
|||
private _shouldHighlightArc = (activeArc: string[], legend?: string): boolean => { |
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.
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.
moved
@@ -2443,22 +2429,6 @@ exports[`DonutChart snapShot testing Should render arc labels in percentage form | |||
onMouseOver={[Function]} | |||
role="img" | |||
/> | |||
<text |
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.
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.
updated snapshots
</div> | ||
</div> | ||
`; | ||
exports[`Donut chart interactions Should reflect theme change 1`] = `<div />`; |
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.
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.
updated snapshots
…icrosoft/fluentui into users/srmukher/LegendsMultiSel
943cff8
to
36e6bdc
Compare
6f40ac0
to
cfd09ea
Compare
const { arc, data, innerRadius, outerRadius, showLabelsInPercent, totalValue, hideLabels, activeArc } = this.props; | ||
private _shouldHighlightArc = (legend?: string): boolean => { | ||
const { activeArc } = this.props; | ||
return !(activeArc && activeArc.length > 0 && legend !== undefined && !activeArc.includes(legend)); |
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.
@@ -94,6 +94,9 @@ export const transformPlotlyJsonToDonutProps = ( | |||
hideLabels, | |||
showLabelsInPercent: firstData.textinfo ? firstData.textinfo === 'percent' : true, | |||
styles, | |||
legendProps: { | |||
canSelectMultipleLegends: true, | |||
}, |
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.
legend props are defined in DeclarativeChart.tsx. Use that only.
dataPointCalloutProps?: IChartDataPoint; | ||
callOutAccessibilityData?: IAccessibilityProps; | ||
selectedLegends: string[]; |
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.
|
||
const getById = queryAllByAttribute.bind(null, 'id'); | ||
expect(getById(container, /Pie.*?first/i)[0]).toHaveStyle('opacity: 1.0'); | ||
expect(getById(container, /Pie.*?second/i)[0]).toHaveStyle('opacity: 1.0'); |
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.
this is good. Thanks.
Legends multi selection for Donut Charts