@@ -5,7 +5,7 @@ import { DonutChartProps } from './DonutChart.types';
5
5
import { useDonutChartStyles } from './useDonutChartStyles.styles' ;
6
6
import { ChartDataPoint } from '../../DonutChart' ;
7
7
import { formatToLocaleString } from '@fluentui/chart-utilities' ;
8
- import { getColorFromToken , getNextColor , MIN_DONUT_RADIUS , useRtl } from '../../utilities/index' ;
8
+ import { areArraysEqual , getColorFromToken , getNextColor , MIN_DONUT_RADIUS , useRtl } from '../../utilities/index' ;
9
9
import { Legend , Legends , LegendContainer } from '../../index' ;
10
10
import { useId } from '@fluentui/react-utilities' ;
11
11
import type { JSXElement } from '@fluentui/react-utilities' ;
@@ -35,22 +35,33 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
35
35
const [ legend , setLegend ] = React . useState < string | undefined > ( '' ) ;
36
36
const [ _width , setWidth ] = React . useState < number | undefined > ( props . width || 200 ) ;
37
37
const [ _height , setHeight ] = React . useState < number | undefined > ( props . height || 200 ) ;
38
- const [ activeLegend , setActiveLegend ] = React . useState < string > ( '' ) ;
38
+ const [ activeLegend , setActiveLegend ] = React . useState < string | undefined > ( undefined ) ;
39
39
const [ color , setColor ] = React . useState < string | undefined > ( '' ) ;
40
40
const [ xCalloutValue , setXCalloutValue ] = React . useState < string > ( '' ) ;
41
41
const [ yCalloutValue , setYCalloutValue ] = React . useState < string > ( '' ) ;
42
- const [ selectedLegend , setSelectedLegend ] = React . useState < string > ( '' ) ;
42
+ const [ selectedLegends , setSelectedLegends ] = React . useState < string [ ] > ( props . legendProps ?. selectedLegends || [ ] ) ;
43
43
const [ focusedArcId , setFocusedArcId ] = React . useState < string > ( '' ) ;
44
44
const [ dataPointCalloutProps , setDataPointCalloutProps ] = React . useState < ChartDataPoint | undefined > ( ) ;
45
45
const [ clickPosition , setClickPosition ] = React . useState ( { x : 0 , y : 0 } ) ;
46
46
const [ isPopoverOpen , setPopoverOpen ] = React . useState ( false ) ;
47
+ const prevPropsRef = React . useRef < DonutChartProps | null > ( null ) ;
47
48
const _legendsRef = React . useRef < LegendContainer > ( null ) ;
48
49
const _isRTL : boolean = useRtl ( ) ;
49
50
50
51
React . useEffect ( ( ) => {
51
52
_fitParentContainer ( ) ;
52
53
} , [ ] ) ;
53
54
55
+ React . useEffect ( ( ) => {
56
+ if ( prevPropsRef . current ) {
57
+ const prevProps = prevPropsRef . current ;
58
+ if ( ! areArraysEqual ( prevProps . legendProps ?. selectedLegends , props . legendProps ?. selectedLegends ) ) {
59
+ setSelectedLegends ( props . legendProps ?. selectedLegends || [ ] ) ;
60
+ }
61
+ }
62
+ prevPropsRef . current = props ;
63
+ } , [ props ] ) ;
64
+
54
65
React . useEffect ( ( ) => {
55
66
if ( prevSize . current . height !== props . height || prevSize . current . width !== props . width ) {
56
67
_fitParentContainer ( ) ;
@@ -98,19 +109,12 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
98
109
const legend : Legend = {
99
110
title : point . legend ! ,
100
111
color,
101
- action : ( ) => {
102
- if ( selectedLegend === point . legend ) {
103
- setSelectedLegend ( '' ) ;
104
- } else {
105
- setSelectedLegend ( point . legend ! ) ;
106
- }
107
- } ,
108
112
hoverAction : ( ) => {
109
113
_handleChartMouseLeave ( ) ;
110
114
setActiveLegend ( point . legend ! ) ;
111
115
} ,
112
116
onMouseOutAction : ( ) => {
113
- setActiveLegend ( '' ) ;
117
+ setActiveLegend ( undefined ) ;
114
118
} ,
115
119
} ;
116
120
return legend ;
@@ -121,11 +125,27 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
121
125
centerLegends
122
126
overflowText = { props . legendsOverflowText }
123
127
{ ...props . legendProps }
128
+ // eslint-disable-next-line react/jsx-no-bind
129
+ onChange = { _onLegendSelectionChange }
124
130
legendRef = { _legendsRef }
125
131
/>
126
132
) ;
127
133
return legends ;
128
134
}
135
+ function _onLegendSelectionChange (
136
+ selectedLegends : string [ ] ,
137
+ event : React . MouseEvent < HTMLButtonElement > ,
138
+ currentLegend ?: Legend ,
139
+ ) : void {
140
+ if ( props . legendProps && props . legendProps ?. canSelectMultipleLegends ) {
141
+ setSelectedLegends ( selectedLegends ) ;
142
+ } else {
143
+ setSelectedLegends ( selectedLegends . slice ( - 1 ) ) ;
144
+ }
145
+ if ( props . legendProps ?. onChange ) {
146
+ props . legendProps . onChange ( selectedLegends , event , currentLegend ) ;
147
+ }
148
+ }
129
149
130
150
function _focusCallback ( data : ChartDataPoint , id : string , e : React . FocusEvent < SVGPathElement > ) : void {
131
151
let cx = 0 ;
@@ -135,7 +155,7 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
135
155
cx = targetRect . left + targetRect . width / 2 ;
136
156
cy = targetRect . top + targetRect . height / 2 ;
137
157
updatePosition ( cx , cy ) ;
138
- setPopoverOpen ( selectedLegend === '' || selectedLegend === data . legend ) ;
158
+ setPopoverOpen ( _noLegendsHighlighted ( ) || _isLegendHighlighted ( data . legend ) ) ;
139
159
setValue ( data . data ! . toString ( ) ) ;
140
160
setLegend ( data . legend ) ;
141
161
setColor ( data . color ! ) ;
@@ -148,7 +168,7 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
148
168
function _hoverCallback ( data : ChartDataPoint , e : React . MouseEvent < SVGPathElement > ) : void {
149
169
if ( _calloutAnchorPoint !== data ) {
150
170
_calloutAnchorPoint = data ;
151
- setPopoverOpen ( selectedLegend === '' || selectedLegend === data . legend ) ;
171
+ setPopoverOpen ( _noLegendsHighlighted ( ) || _isLegendHighlighted ( data . legend ) ) ;
152
172
setValue ( data . data ! . toString ( ) ) ;
153
173
setLegend ( data . legend ) ;
154
174
setColor ( data . color ! ) ;
@@ -172,16 +192,22 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
172
192
}
173
193
174
194
function _valueInsideDonut ( valueInsideDonut : string | number | undefined , data : ChartDataPoint [ ] ) {
175
- const highlightedLegend = _getHighlightedLegend ( ) ;
176
- if ( valueInsideDonut !== undefined && ( highlightedLegend !== '' || isPopoverOpen ) ) {
177
- let legendValue = valueInsideDonut ;
178
- data ! . map ( ( point : ChartDataPoint , index : number ) => {
179
- if ( point . legend === highlightedLegend || ( isPopoverOpen && point . legend === legend ) ) {
180
- legendValue = point . yAxisCalloutData ? point . yAxisCalloutData : point . data ! ;
195
+ const highlightedLegends = _getHighlightedLegend ( ) ;
196
+ if ( valueInsideDonut !== undefined && ( highlightedLegends . length === 1 || isPopoverOpen ) ) {
197
+ const pointValue = data . find ( point => _isLegendHighlighted ( point . legend ) ) ;
198
+ return pointValue
199
+ ? pointValue . yAxisCalloutData
200
+ ? pointValue . yAxisCalloutData
201
+ : pointValue . data !
202
+ : valueInsideDonut ;
203
+ } else if ( highlightedLegends . length > 0 ) {
204
+ let totalValue = 0 ;
205
+ data . forEach ( point => {
206
+ if ( highlightedLegends . includes ( point . legend ! ) ) {
207
+ totalValue += point . data ! ;
181
208
}
182
- return ;
183
209
} ) ;
184
- return legendValue ;
210
+ return totalValue ;
185
211
} else {
186
212
return valueInsideDonut ;
187
213
}
@@ -199,10 +225,17 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
199
225
* This function returns
200
226
* the selected legend if there is one
201
227
* or the hovered legend if none of the legends is selected.
202
- * Note: This won't work in case of multiple legends selection.
203
228
*/
204
229
function _getHighlightedLegend ( ) {
205
- return selectedLegend || activeLegend ;
230
+ return selectedLegends . length > 0 ? selectedLegends : activeLegend ? [ activeLegend ] : [ ] ;
231
+ }
232
+
233
+ function _isLegendHighlighted ( legend : string | undefined ) : boolean {
234
+ return _getHighlightedLegend ( ) . includes ( legend ! ) ;
235
+ }
236
+
237
+ function _noLegendsHighlighted ( ) : boolean {
238
+ return _getHighlightedLegend ( ) . length === 0 ;
206
239
}
207
240
208
241
function _isChartEmpty ( ) : boolean {
0 commit comments