1
+ import type { ReactNode } from 'react' ;
1
2
import { Fragment , useState } from 'react' ;
2
3
import { pie } from 'd3-shape' ;
3
4
import {
@@ -9,16 +10,22 @@ import {
9
10
useChartContext ,
10
11
THIN_ARC_CORNER_THICKNESS ,
11
12
isInfinity ,
13
+ DataType ,
14
+ ChartMargin ,
15
+ InternalChartType ,
12
16
} from '@shopify/polaris-viz-core' ;
13
17
import type {
14
18
DataPoint ,
15
19
DataSeries ,
16
20
LabelFormatter ,
17
21
Direction ,
22
+ BoundingRect ,
18
23
} from '@shopify/polaris-viz-core' ;
19
24
20
25
import { getAnimationDelayForItems } from '../../utilities/getAnimationDelayForItems' ;
21
26
import { getContainerAlignmentForLegend } from '../../utilities' ;
27
+ import { useDonutChartTooltipContents } from '../../hooks/useDonutChartTooltipContents' ;
28
+ import { TooltipWrapper } from '../../components/TooltipWrapper' ;
22
29
import type { ComparisonMetricProps } from '../ComparisonMetric' ;
23
30
import { LegendContainer , useLegend } from '../../components/LegendContainer' ;
24
31
import {
@@ -33,6 +40,7 @@ import type {
33
40
RenderHiddenLegendLabel ,
34
41
RenderInnerValueContent ,
35
42
RenderLegendContent ,
43
+ RenderTooltipContentData ,
36
44
} from '../../types' ;
37
45
import { ChartSkeleton } from '../../components/ChartSkeleton' ;
38
46
@@ -59,6 +67,7 @@ export interface ChartProps {
59
67
renderInnerValueContent ?: RenderInnerValueContent ;
60
68
renderLegendContent ?: RenderLegendContent ;
61
69
renderHiddenLegendLabel ?: RenderHiddenLegendLabel ;
70
+ renderTooltipContent ?: ( data : RenderTooltipContentData ) => ReactNode ;
62
71
total ?: number ;
63
72
}
64
73
@@ -78,12 +87,14 @@ export function Chart({
78
87
renderLegendContent,
79
88
renderHiddenLegendLabel,
80
89
seriesNameFormatter,
90
+ renderTooltipContent,
81
91
total,
82
92
} : ChartProps ) {
83
93
const { shouldAnimate, containerBounds} = useChartContext ( ) ;
84
94
const chartId = useUniqueId ( 'Donut' ) ;
85
95
const [ activeIndex , setActiveIndex ] = useState < number > ( - 1 ) ;
86
96
const selectedTheme = useTheme ( ) ;
97
+ const [ svgRef , setSvgRef ] = useState < SVGSVGElement | null > ( null ) ;
87
98
88
99
const seriesCount = clamp ( {
89
100
amount : data . length ,
@@ -92,6 +103,19 @@ export function Chart({
92
103
93
104
const seriesColor = getSeriesColors ( seriesCount , selectedTheme ) ;
94
105
106
+ const chartBounds : BoundingRect = {
107
+ width : containerBounds . width ,
108
+ height : containerBounds . height ,
109
+ x : 0 ,
110
+ y : 0 ,
111
+ } ;
112
+
113
+ const getTooltipMarkup = useDonutChartTooltipContents ( {
114
+ renderTooltipContent,
115
+ data,
116
+ seriesColors : seriesColor ,
117
+ } ) ;
118
+
95
119
const legendDirection : Direction =
96
120
legendPosition === 'right' || legendPosition === 'left'
97
121
? 'vertical'
@@ -100,19 +124,16 @@ export function Chart({
100
124
const maxLegendWidth =
101
125
legendDirection === 'vertical' ? containerBounds . width / 2 : 0 ;
102
126
103
- const { height, width, legend, setLegendDimensions, isLegendMounted} =
104
- useLegend ( {
105
- data : [ { series : data , shape : 'Bar' } ] ,
106
- showLegend,
107
- direction : legendDirection ,
108
- colors : seriesColor ,
109
- maxWidth : maxLegendWidth ,
110
- seriesNameFormatter,
111
- } ) ;
127
+ const { height, width, legend, setLegendDimensions} = useLegend ( {
128
+ data : [ { series : data , shape : 'Bar' } ] ,
129
+ showLegend,
130
+ direction : legendDirection ,
131
+ colors : seriesColor ,
132
+ maxWidth : maxLegendWidth ,
133
+ seriesNameFormatter,
134
+ } ) ;
112
135
113
- const shouldUseColorVisionEvents = Boolean (
114
- width && height && isLegendMounted ,
115
- ) ;
136
+ const shouldUseColorVisionEvents = Boolean ( width && height ) ;
116
137
117
138
useColorVisionEvents ( {
118
139
enabled : shouldUseColorVisionEvents ,
@@ -208,60 +229,59 @@ export function Chart({
208
229
viewBox = { `${ minX } ${ minY } ${ viewBoxDimensions . width } ${ viewBoxDimensions . height } ` }
209
230
height = { diameter }
210
231
width = { diameter }
232
+ ref = { setSvgRef }
211
233
>
212
- { isLegendMounted && (
213
- < g className = { styles . DonutChart } >
214
- { emptyState ? (
215
- < g aria-hidden >
216
- < Arc
217
- isAnimated = { shouldAnimate }
218
- width = { diameter }
219
- height = { diameter }
220
- radius = { radius }
221
- startAngle = { 0 }
222
- endAngle = { FULL_CIRCLE }
223
- color = { selectedTheme . grid . color }
224
- cornerRadius = { selectedTheme . arc . cornerRadius }
225
- thickness = { thickness }
226
- />
227
- </ g >
228
- ) : (
229
- pieChartData . map (
230
- ( { data : pieData , startAngle, endAngle} , index ) => {
231
- const color = data [ index ] ?. color ?? seriesColor [ index ] ;
232
- const name = data [ index ] . name ;
233
- const accessibilityLabel = `${ name } : ${ pieData . key } - ${ pieData . value } ` ;
234
+ < g className = { styles . DonutChart } >
235
+ { emptyState ? (
236
+ < g aria-hidden >
237
+ < Arc
238
+ isAnimated = { shouldAnimate }
239
+ width = { diameter }
240
+ height = { diameter }
241
+ radius = { radius }
242
+ startAngle = { 0 }
243
+ endAngle = { FULL_CIRCLE }
244
+ color = { selectedTheme . grid . color }
245
+ cornerRadius = { selectedTheme . arc . cornerRadius }
246
+ thickness = { thickness }
247
+ />
248
+ </ g >
249
+ ) : (
250
+ pieChartData . map (
251
+ ( { data : pieData , startAngle, endAngle} , index ) => {
252
+ const color = data [ index ] ?. color ?? seriesColor [ index ] ;
253
+ const name = data [ index ] . name ;
254
+ const accessibilityLabel = `${ name } : ${ pieData . key } - ${ pieData . value } ` ;
234
255
235
- return (
236
- < g
237
- key = { `${ chartId } -arc-${ index } ` }
238
- className = { styles . DonutChart }
239
- aria-label = { accessibilityLabel }
240
- role = "img"
241
- >
242
- < Arc
243
- isAnimated = { shouldAnimate }
244
- animationDelay = { getAnimationDelayForItems (
245
- pieChartData . length ,
246
- ) }
247
- index = { index }
248
- activeIndex = { activeIndex }
249
- width = { diameter }
250
- height = { diameter }
251
- radius = { radius }
252
- startAngle = { startAngle }
253
- endAngle = { endAngle }
254
- color = { color }
255
- cornerRadius = { selectedTheme . arc . cornerRadius }
256
- thickness = { thickness }
257
- />
258
- </ g >
259
- ) ;
260
- } ,
261
- )
262
- ) }
263
- </ g >
264
- ) }
256
+ return (
257
+ < g
258
+ key = { `${ chartId } -arc-${ index } ` }
259
+ className = { styles . DonutChart }
260
+ aria-label = { accessibilityLabel }
261
+ role = "img"
262
+ >
263
+ < Arc
264
+ isAnimated = { shouldAnimate }
265
+ animationDelay = { getAnimationDelayForItems (
266
+ pieChartData . length ,
267
+ ) }
268
+ index = { index }
269
+ activeIndex = { activeIndex }
270
+ width = { diameter }
271
+ height = { diameter }
272
+ radius = { radius }
273
+ startAngle = { startAngle }
274
+ endAngle = { endAngle }
275
+ color = { color }
276
+ cornerRadius = { selectedTheme . arc . cornerRadius }
277
+ thickness = { thickness }
278
+ />
279
+ </ g >
280
+ ) ;
281
+ } ,
282
+ )
283
+ ) }
284
+ </ g >
265
285
</ svg >
266
286
< InnerValue
267
287
activeValue = { activeValue }
@@ -300,6 +320,16 @@ export function Chart({
300
320
}
301
321
/>
302
322
) }
323
+ < TooltipWrapper
324
+ chartBounds = { chartBounds }
325
+ chartType = { InternalChartType . Donut }
326
+ focusElementDataType = { DataType . Arc }
327
+ forceActiveIndex = { activeIndex }
328
+ getMarkup = { getTooltipMarkup }
329
+ margin = { ChartMargin }
330
+ parentElement = { svgRef }
331
+ usePortal
332
+ />
303
333
</ div >
304
334
) ;
305
335
}
0 commit comments