@automattic/charts 0.56.2 → 0.56.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/charts/bar-chart/index.cjs +5 -5
  3. package/dist/charts/bar-chart/index.css +12 -0
  4. package/dist/charts/bar-chart/index.css.map +1 -1
  5. package/dist/charts/bar-chart/index.js +4 -4
  6. package/dist/charts/bar-list-chart/index.cjs +6 -6
  7. package/dist/charts/bar-list-chart/index.css +12 -0
  8. package/dist/charts/bar-list-chart/index.css.map +1 -1
  9. package/dist/charts/bar-list-chart/index.js +5 -5
  10. package/dist/charts/conversion-funnel-chart/index.cjs +5 -3
  11. package/dist/charts/conversion-funnel-chart/index.cjs.map +1 -1
  12. package/dist/charts/conversion-funnel-chart/index.css +14 -1
  13. package/dist/charts/conversion-funnel-chart/index.css.map +1 -1
  14. package/dist/charts/conversion-funnel-chart/index.d.cts +2 -0
  15. package/dist/charts/conversion-funnel-chart/index.d.ts +2 -0
  16. package/dist/charts/conversion-funnel-chart/index.js +4 -2
  17. package/dist/charts/geo-chart/index.cjs +4 -4
  18. package/dist/charts/geo-chart/index.css +12 -0
  19. package/dist/charts/geo-chart/index.css.map +1 -1
  20. package/dist/charts/geo-chart/index.js +3 -3
  21. package/dist/charts/leaderboard-chart/index.cjs +5 -5
  22. package/dist/charts/leaderboard-chart/index.css +12 -0
  23. package/dist/charts/leaderboard-chart/index.css.map +1 -1
  24. package/dist/charts/leaderboard-chart/index.js +4 -4
  25. package/dist/charts/line-chart/index.cjs +5 -5
  26. package/dist/charts/line-chart/index.css +12 -0
  27. package/dist/charts/line-chart/index.css.map +1 -1
  28. package/dist/charts/line-chart/index.js +4 -4
  29. package/dist/charts/pie-chart/index.cjs +7 -7
  30. package/dist/charts/pie-chart/index.css +12 -0
  31. package/dist/charts/pie-chart/index.css.map +1 -1
  32. package/dist/charts/pie-chart/index.js +6 -6
  33. package/dist/charts/pie-semi-circle-chart/index.cjs +7 -7
  34. package/dist/charts/pie-semi-circle-chart/index.css +12 -0
  35. package/dist/charts/pie-semi-circle-chart/index.css.map +1 -1
  36. package/dist/charts/pie-semi-circle-chart/index.js +6 -6
  37. package/dist/charts/sparkline/index.cjs +6 -6
  38. package/dist/charts/sparkline/index.css +12 -0
  39. package/dist/charts/sparkline/index.css.map +1 -1
  40. package/dist/charts/sparkline/index.js +5 -5
  41. package/dist/{chunk-OTZT3MC2.cjs → chunk-2A34OA5O.cjs} +19 -20
  42. package/dist/chunk-2A34OA5O.cjs.map +1 -0
  43. package/dist/chunk-4YYROZDJ.cjs +375 -0
  44. package/dist/chunk-4YYROZDJ.cjs.map +1 -0
  45. package/dist/{chunk-YYQ4IK5V.cjs → chunk-5N77S5N3.cjs} +103 -80
  46. package/dist/chunk-5N77S5N3.cjs.map +1 -0
  47. package/dist/chunk-66BXSWMW.cjs +1065 -0
  48. package/dist/chunk-66BXSWMW.cjs.map +1 -0
  49. package/dist/{chunk-CEZGL6YP.js → chunk-6CCZL2JJ.js} +15 -7
  50. package/dist/chunk-6CCZL2JJ.js.map +1 -0
  51. package/dist/{chunk-NW3RUYK2.cjs → chunk-7QDEU3KN.cjs} +15 -22
  52. package/dist/chunk-7QDEU3KN.cjs.map +1 -0
  53. package/dist/{chunk-H34CJSR6.js → chunk-AWNCAKZY.js} +367 -358
  54. package/dist/chunk-AWNCAKZY.js.map +1 -0
  55. package/dist/{chunk-5XI443YP.js → chunk-BPYKWMI7.js} +72 -64
  56. package/dist/chunk-BPYKWMI7.js.map +1 -0
  57. package/dist/{chunk-7UJPVCMB.cjs → chunk-CERFRCXD.cjs} +265 -262
  58. package/dist/chunk-CERFRCXD.cjs.map +1 -0
  59. package/dist/chunk-CMHPXSCI.js +351 -0
  60. package/dist/chunk-CMHPXSCI.js.map +1 -0
  61. package/dist/chunk-EBDUXL5K.js +421 -0
  62. package/dist/chunk-EBDUXL5K.js.map +1 -0
  63. package/dist/{chunk-2VPPTJS2.js → chunk-FZYJM5PN.js} +256 -253
  64. package/dist/chunk-FZYJM5PN.js.map +1 -0
  65. package/dist/chunk-GBDFC74U.cjs +165 -0
  66. package/dist/chunk-GBDFC74U.cjs.map +1 -0
  67. package/dist/{chunk-ODF5O5PV.cjs → chunk-HNEG3EFJ.cjs} +154 -170
  68. package/dist/chunk-HNEG3EFJ.cjs.map +1 -0
  69. package/dist/{chunk-SRXJLAKG.cjs → chunk-I2276W3I.cjs} +28 -37
  70. package/dist/chunk-I2276W3I.cjs.map +1 -0
  71. package/dist/chunk-KKPZ4MVF.js +375 -0
  72. package/dist/chunk-KKPZ4MVF.js.map +1 -0
  73. package/dist/chunk-KMYJJTSR.cjs +421 -0
  74. package/dist/chunk-KMYJJTSR.cjs.map +1 -0
  75. package/dist/{chunk-A3AEEGKR.js → chunk-KXRWNFQJ.js} +20 -21
  76. package/dist/chunk-KXRWNFQJ.js.map +1 -0
  77. package/dist/{chunk-TVV7ZI7C.cjs → chunk-LSV7F26B.cjs} +362 -353
  78. package/dist/chunk-LSV7F26B.cjs.map +1 -0
  79. package/dist/{chunk-T4J6TI55.js → chunk-M7PRGJFE.js} +102 -79
  80. package/dist/chunk-M7PRGJFE.js.map +1 -0
  81. package/dist/{chunk-TNRKEBTA.js → chunk-PGJAZN2H.js} +148 -164
  82. package/dist/{chunk-TNRKEBTA.js.map → chunk-PGJAZN2H.js.map} +1 -1
  83. package/dist/chunk-R23BFDIW.js +1065 -0
  84. package/dist/chunk-R23BFDIW.js.map +1 -0
  85. package/dist/{chunk-HIWNB5PK.cjs → chunk-RCY6XLGU.cjs} +13 -5
  86. package/dist/chunk-RCY6XLGU.cjs.map +1 -0
  87. package/dist/chunk-RSYD434G.cjs +351 -0
  88. package/dist/chunk-RSYD434G.cjs.map +1 -0
  89. package/dist/{chunk-C33AQZEC.js → chunk-TYIH5LMV.js} +16 -23
  90. package/dist/chunk-TYIH5LMV.js.map +1 -0
  91. package/dist/chunk-WMWAUOQ4.js +165 -0
  92. package/dist/chunk-WMWAUOQ4.js.map +1 -0
  93. package/dist/chunk-XWYZIFZW.js +66 -0
  94. package/dist/chunk-XWYZIFZW.js.map +1 -0
  95. package/dist/{chunk-7HROSZRS.cjs → chunk-Y3NNQMAX.cjs} +70 -62
  96. package/dist/chunk-Y3NNQMAX.cjs.map +1 -0
  97. package/dist/chunk-ZXEFMKVP.cjs +120 -0
  98. package/dist/chunk-ZXEFMKVP.cjs.map +1 -0
  99. package/dist/chunk-ZY4FXLMM.js +120 -0
  100. package/dist/chunk-ZY4FXLMM.js.map +1 -0
  101. package/dist/components/legend/index.cjs +2 -2
  102. package/dist/components/legend/index.css +12 -0
  103. package/dist/components/legend/index.css.map +1 -1
  104. package/dist/components/legend/index.js +1 -1
  105. package/dist/components/tooltip/index.cjs +2 -2
  106. package/dist/components/tooltip/index.js +1 -1
  107. package/dist/components/trend-indicator/index.cjs +2 -2
  108. package/dist/components/trend-indicator/index.js +1 -1
  109. package/dist/hooks/index.cjs +4 -2
  110. package/dist/hooks/index.cjs.map +1 -1
  111. package/dist/hooks/index.css +12 -0
  112. package/dist/hooks/index.css.map +1 -1
  113. package/dist/hooks/index.d.cts +28 -2
  114. package/dist/hooks/index.d.ts +28 -2
  115. package/dist/hooks/index.js +3 -1
  116. package/dist/index.cjs +18 -18
  117. package/dist/index.cjs.map +1 -1
  118. package/dist/index.css +14 -1
  119. package/dist/index.css.map +1 -1
  120. package/dist/index.d.cts +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.js +17 -17
  123. package/dist/providers/index.cjs +2 -2
  124. package/dist/providers/index.css +12 -0
  125. package/dist/providers/index.css.map +1 -1
  126. package/dist/providers/index.d.cts +1 -1
  127. package/dist/providers/index.d.ts +1 -1
  128. package/dist/providers/index.js +1 -1
  129. package/dist/{themes-DQs9rbN5.d.cts → themes-BDVaIfBz.d.cts} +9 -0
  130. package/dist/{themes-CRV5fVzJ.d.ts → themes-mcS8QNkQ.d.ts} +9 -0
  131. package/package.json +7 -4
  132. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +2 -1
  133. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +16 -6
  134. package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +34 -0
  135. package/src/charts/conversion-funnel-chart/types.ts +2 -0
  136. package/src/charts/pie-chart/pie-chart.tsx +2 -3
  137. package/src/hooks/index.ts +1 -0
  138. package/src/hooks/test/use-tooltip-portal-relocator.test.ts +216 -0
  139. package/src/hooks/use-tooltip-portal-relocator.module.scss +10 -0
  140. package/src/hooks/use-tooltip-portal-relocator.ts +177 -0
  141. package/src/providers/chart-context/global-charts-provider.tsx +18 -1
  142. package/tsup.config.ts +11 -0
  143. package/dist/chunk-2VPPTJS2.js.map +0 -1
  144. package/dist/chunk-5XI443YP.js.map +0 -1
  145. package/dist/chunk-7HROSZRS.cjs.map +0 -1
  146. package/dist/chunk-7UJPVCMB.cjs.map +0 -1
  147. package/dist/chunk-A3AEEGKR.js.map +0 -1
  148. package/dist/chunk-C33AQZEC.js.map +0 -1
  149. package/dist/chunk-CEZGL6YP.js.map +0 -1
  150. package/dist/chunk-COOC2TVQ.js +0 -167
  151. package/dist/chunk-COOC2TVQ.js.map +0 -1
  152. package/dist/chunk-EJHLLXBV.js +0 -362
  153. package/dist/chunk-EJHLLXBV.js.map +0 -1
  154. package/dist/chunk-FWMJ2FR2.js +0 -121
  155. package/dist/chunk-FWMJ2FR2.js.map +0 -1
  156. package/dist/chunk-GRYNIPWH.cjs +0 -385
  157. package/dist/chunk-GRYNIPWH.cjs.map +0 -1
  158. package/dist/chunk-H34CJSR6.js.map +0 -1
  159. package/dist/chunk-HIWNB5PK.cjs.map +0 -1
  160. package/dist/chunk-IZWC33YN.cjs +0 -357
  161. package/dist/chunk-IZWC33YN.cjs.map +0 -1
  162. package/dist/chunk-KOF32DBL.cjs +0 -1058
  163. package/dist/chunk-KOF32DBL.cjs.map +0 -1
  164. package/dist/chunk-LHWRZMF7.cjs +0 -362
  165. package/dist/chunk-LHWRZMF7.cjs.map +0 -1
  166. package/dist/chunk-MFRS2PEY.cjs +0 -121
  167. package/dist/chunk-MFRS2PEY.cjs.map +0 -1
  168. package/dist/chunk-MMDLXS6O.js +0 -75
  169. package/dist/chunk-MMDLXS6O.js.map +0 -1
  170. package/dist/chunk-NW3RUYK2.cjs.map +0 -1
  171. package/dist/chunk-ODF5O5PV.cjs.map +0 -1
  172. package/dist/chunk-OTZT3MC2.cjs.map +0 -1
  173. package/dist/chunk-SBRMWDWM.js +0 -357
  174. package/dist/chunk-SBRMWDWM.js.map +0 -1
  175. package/dist/chunk-SRXJLAKG.cjs.map +0 -1
  176. package/dist/chunk-T4J6TI55.js.map +0 -1
  177. package/dist/chunk-TVV7ZI7C.cjs.map +0 -1
  178. package/dist/chunk-XVMXWV3C.cjs +0 -167
  179. package/dist/chunk-XVMXWV3C.cjs.map +0 -1
  180. package/dist/chunk-YYQ4IK5V.cjs.map +0 -1
  181. package/dist/chunk-ZDNCF642.js +0 -1058
  182. package/dist/chunk-ZDNCF642.js.map +0 -1
  183. package/dist/chunk-ZWBUEHKF.js +0 -385
  184. package/dist/chunk-ZWBUEHKF.js.map +0 -1
@@ -0,0 +1,216 @@
1
+ /* eslint-disable testing-library/no-node-access */
2
+ import { renderHook } from '@testing-library/react';
3
+ import { useTooltipPortalRelocator } from '../use-tooltip-portal-relocator';
4
+
5
+ // In the production build, CSS module class names are hashed (e.g. "a8ccharts-abc123").
6
+ // In jest, the SCSS module import is stubbed to a filename string, so
7
+ // styles.relocatedPortal resolves to undefined and classList.add() is a no-op.
8
+ // We mock the module to return a proper class map so we can assert on class names.
9
+ jest.mock( '../use-tooltip-portal-relocator.module.scss', () => ( {
10
+ __esModule: true,
11
+ default: { relocatedPortal: 'relocatedPortal' },
12
+ } ) );
13
+
14
+ /**
15
+ * Create a mock visx tooltip portal node for testing.
16
+ * @return {HTMLDivElement} A div mimicking a visx tooltip portal.
17
+ */
18
+ function createVisxPortalNode(): HTMLDivElement {
19
+ const portal = document.createElement( 'div' );
20
+ const child = document.createElement( 'div' );
21
+ child.className = 'visx-tooltip';
22
+ portal.appendChild( child );
23
+ return portal;
24
+ }
25
+
26
+ /**
27
+ * Sets up a container, ref, and renders the hook.
28
+ * Optionally appends a visx portal node to document.body before rendering.
29
+ * @param options - Setup options.
30
+ * @param options.withPortal - If true, creates and appends a visx portal before rendering.
31
+ * @return Setup result with container, ref, unmount, and optionally the portal node.
32
+ */
33
+ function setupHook( { withPortal = false } = {} ) {
34
+ const container = document.createElement( 'div' );
35
+ document.body.appendChild( container );
36
+
37
+ let portal: HTMLDivElement | undefined;
38
+ if ( withPortal ) {
39
+ portal = createVisxPortalNode();
40
+ document.body.appendChild( portal );
41
+ }
42
+
43
+ const ref = { current: container };
44
+ const { unmount } = renderHook( () => useTooltipPortalRelocator( ref ) );
45
+
46
+ return { container, ref, unmount, portal };
47
+ }
48
+
49
+ describe( 'useTooltipPortalRelocator', () => {
50
+ let nativeRemoveChild: typeof document.body.removeChild;
51
+
52
+ beforeAll( () => {
53
+ nativeRemoveChild = document.body.removeChild;
54
+ } );
55
+
56
+ afterEach( () => {
57
+ // Restore native removeChild and clear all body children to prevent
58
+ // leaked portals from interfering with subsequent tests.
59
+ document.body.removeChild = nativeRemoveChild;
60
+ while ( document.body.firstChild ) {
61
+ nativeRemoveChild.call( document.body, document.body.firstChild );
62
+ }
63
+ } );
64
+
65
+ test( 'does nothing when containerRef is undefined', () => {
66
+ const { unmount } = renderHook( () => useTooltipPortalRelocator( undefined ) );
67
+ const portal = createVisxPortalNode();
68
+ document.body.appendChild( portal );
69
+ expect( portal.parentNode ).toBe( document.body );
70
+ unmount();
71
+ } );
72
+
73
+ test( 'does nothing when containerRef.current is null', () => {
74
+ const nullRef = { current: null };
75
+ const { unmount } = renderHook( () => useTooltipPortalRelocator( nullRef ) );
76
+ const portal = createVisxPortalNode();
77
+ document.body.appendChild( portal );
78
+ expect( portal.parentNode ).toBe( document.body );
79
+ unmount();
80
+ } );
81
+
82
+ test( 'relocates existing visx portal nodes into the container', () => {
83
+ const { container, unmount, portal } = setupHook( { withPortal: true } );
84
+ expect( portal!.parentNode ).toBe( container );
85
+ unmount();
86
+ } );
87
+
88
+ test( 'applies relocated-portal class to relocated portals', () => {
89
+ const { unmount, portal } = setupHook( { withPortal: true } );
90
+ expect( portal! ).toHaveClass( 'relocatedPortal' );
91
+ unmount();
92
+ } );
93
+
94
+ test( 'does not relocate newly added non-visx nodes', async () => {
95
+ const { unmount } = setupHook();
96
+
97
+ const regularDiv = document.createElement( 'div' );
98
+ regularDiv.id = 'some-id';
99
+ document.body.appendChild( regularDiv );
100
+
101
+ // MutationObserver is async — wait for microtask
102
+ await new Promise( resolve => setTimeout( resolve, 0 ) );
103
+
104
+ expect( regularDiv.parentNode ).toBe( document.body );
105
+ unmount();
106
+ } );
107
+
108
+ test( 'observes and relocates newly added portal nodes', async () => {
109
+ const { container, unmount } = setupHook();
110
+
111
+ const portal = createVisxPortalNode();
112
+ document.body.appendChild( portal );
113
+
114
+ // MutationObserver is async — wait for microtask
115
+ await new Promise( resolve => setTimeout( resolve, 0 ) );
116
+
117
+ expect( portal.parentNode ).toBe( container );
118
+ unmount();
119
+ } );
120
+
121
+ test( 'patched removeChild handles relocated nodes without throwing', () => {
122
+ const { unmount, portal } = setupHook( { withPortal: true } );
123
+
124
+ // Portal is now in container, but visx will call document.body.removeChild(portal)
125
+ expect( () => document.body.removeChild( portal! ) ).not.toThrow();
126
+ expect( portal!.parentNode ).toBeNull();
127
+ unmount();
128
+ } );
129
+
130
+ test( 'patched removeChild delegates to original for non-relocated nodes', () => {
131
+ const { unmount } = setupHook();
132
+
133
+ const regularDiv = document.createElement( 'div' );
134
+ document.body.appendChild( regularDiv );
135
+ expect( () => document.body.removeChild( regularDiv ) ).not.toThrow();
136
+ expect( regularDiv.parentNode ).toBeNull();
137
+ unmount();
138
+ } );
139
+
140
+ test( 'cleanup restores removeChild when it has not been wrapped by others', () => {
141
+ const originalRemoveChild = document.body.removeChild;
142
+ const { unmount } = setupHook();
143
+
144
+ // removeChild should be patched
145
+ expect( document.body.removeChild ).not.toBe( originalRemoveChild );
146
+
147
+ unmount();
148
+
149
+ // Should be restored
150
+ expect( document.body.removeChild ).toBe( originalRemoveChild );
151
+ } );
152
+
153
+ test( 'cleanup leaves removeChild when another wrapper was installed after ours', () => {
154
+ const { unmount } = setupHook();
155
+
156
+ // Simulate another library wrapping removeChild after our patch
157
+ const ourPatch = document.body.removeChild;
158
+ const thirdPartyWrapper = function < T extends Node >( child: T ): T {
159
+ return ourPatch.call( document.body, child );
160
+ };
161
+ document.body.removeChild = thirdPartyWrapper;
162
+
163
+ unmount();
164
+
165
+ // Should NOT restore — third party wrapper is still in place
166
+ expect( document.body.removeChild ).toBe( thirdPartyWrapper );
167
+ } );
168
+
169
+ test( 'cleanup moves relocated nodes back to document.body', () => {
170
+ const { container, unmount, portal } = setupHook( { withPortal: true } );
171
+
172
+ expect( portal!.parentNode ).toBe( container );
173
+
174
+ unmount();
175
+
176
+ // Node should be moved back to body on cleanup
177
+ expect( portal!.parentNode ).toBe( document.body );
178
+ } );
179
+
180
+ test( 'cleanup removes relocated-portal class from nodes', () => {
181
+ const { unmount, portal } = setupHook( { withPortal: true } );
182
+
183
+ expect( portal! ).toHaveClass( 'relocatedPortal' );
184
+
185
+ unmount();
186
+
187
+ expect( portal! ).not.toHaveClass( 'relocatedPortal' );
188
+ } );
189
+
190
+ test( 'ref-counting allows multiple instances to share the patch', () => {
191
+ const container1 = document.createElement( 'div' );
192
+ const container2 = document.createElement( 'div' );
193
+ document.body.appendChild( container1 );
194
+ document.body.appendChild( container2 );
195
+
196
+ const ref1 = { current: container1 };
197
+ const ref2 = { current: container2 };
198
+
199
+ const originalRemoveChild = document.body.removeChild;
200
+
201
+ const { unmount: unmountFirst } = renderHook( () => useTooltipPortalRelocator( ref1 ) );
202
+ const patchedFn = document.body.removeChild;
203
+ const { unmount: unmountSecond } = renderHook( () => useTooltipPortalRelocator( ref2 ) );
204
+
205
+ // Both should share the same patched removeChild
206
+ expect( document.body.removeChild ).toBe( patchedFn );
207
+
208
+ // Unmounting the first should keep the patch (ref count > 0)
209
+ unmountFirst();
210
+ expect( document.body.removeChild ).toBe( patchedFn );
211
+
212
+ // Unmounting the second should restore the original
213
+ unmountSecond();
214
+ expect( document.body.removeChild ).toBe( originalRemoveChild );
215
+ } );
216
+ } );
@@ -0,0 +1,10 @@
1
+ .relocatedPortal {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 0;
6
+ height: 0;
7
+ overflow: visible;
8
+ z-index: 1;
9
+ pointer-events: none;
10
+ }
@@ -0,0 +1,177 @@
1
+ import { useEffect } from 'react';
2
+ import styles from './use-tooltip-portal-relocator.module.scss';
3
+ import type { RefObject } from 'react';
4
+
5
+ /**
6
+ * Detects whether a DOM node is a visx chart tooltip portal.
7
+ *
8
+ * visx renders tooltips via `ReactDOM.createPortal` into plain `<div>` elements
9
+ * appended to `document.body`. These portals have no id or className and contain
10
+ * a child element with the class `visx-tooltip`.
11
+ * @param node - The DOM node to check.
12
+ * @return Whether the node is a visx tooltip portal div.
13
+ */
14
+ function isVisxPortalNode( node: Node ): node is HTMLDivElement {
15
+ return (
16
+ node instanceof HTMLDivElement &&
17
+ node.parentElement === document.body &&
18
+ ! node.id &&
19
+ ! node.className &&
20
+ node.querySelector( '.visx-tooltip' ) !== null
21
+ );
22
+ }
23
+
24
+ // Shared state for the document.body.removeChild patch.
25
+ // Reference-counted so multiple hook instances can coexist safely.
26
+ let patchRefCount = 0;
27
+ let origRemoveChild: typeof document.body.removeChild | null = null;
28
+ let patchedRemoveChild: typeof document.body.removeChild | null = null;
29
+ const relocatedNodes = new WeakSet< Node >();
30
+
31
+ /**
32
+ * Installs (or increments the ref count of) the shared removeChild patch.
33
+ */
34
+ function installRemoveChildPatch() {
35
+ if ( patchRefCount++ > 0 ) {
36
+ return;
37
+ }
38
+ origRemoveChild = document.body.removeChild;
39
+ patchedRemoveChild = function < T extends Node >( this: HTMLElement, child: T ): T {
40
+ if ( relocatedNodes.has( child ) && child.parentNode !== this ) {
41
+ relocatedNodes.delete( child );
42
+ child.parentNode?.removeChild( child );
43
+ return child;
44
+ }
45
+ return origRemoveChild!.call( this, child );
46
+ };
47
+ document.body.removeChild = patchedRemoveChild;
48
+ }
49
+
50
+ /**
51
+ * Decrements the ref count and removes the patch when no instances remain.
52
+ * If another library has since wrapped our patch, we leave it in place to
53
+ * avoid breaking their chain — our function becomes a transparent pass-through
54
+ * once all relocated nodes have been cleaned up.
55
+ */
56
+ function uninstallRemoveChildPatch() {
57
+ if ( --patchRefCount > 0 ) {
58
+ return;
59
+ }
60
+ // Only revert if removeChild is still our function. If something else
61
+ // has wrapped it, reverting would break their patch.
62
+ if ( document.body.removeChild === patchedRemoveChild ) {
63
+ document.body.removeChild = origRemoveChild!;
64
+ }
65
+ origRemoveChild = null;
66
+ patchedRemoveChild = null;
67
+ }
68
+
69
+ /**
70
+ * Relocates visx chart tooltip portals from `document.body` into a target
71
+ * container element. This allows the tooltips to participate in the same CSS
72
+ * stacking context as other elements in the container (e.g. a sticky header),
73
+ * so z-index ordering works correctly between them.
74
+ *
75
+ * The relocated portal divs use `position: fixed` at the viewport origin to
76
+ * preserve the tooltip coordinate system (visx calculates positions relative
77
+ * to the viewport).
78
+ *
79
+ * Because the visx Portal class calls `document.body.removeChild(node)` during
80
+ * unmount, we patch `document.body.removeChild` to gracefully handle nodes that
81
+ * were moved out of body. Without this, React throws a "not a child of this
82
+ * node" error when tooltips unmount.
83
+ *
84
+ * **Important:** The container and its ancestors must not have CSS `transform`,
85
+ * `perspective`, or `filter` properties set, as these create a new containing
86
+ * block for `position: fixed` children, breaking viewport-relative positioning.
87
+ *
88
+ * @param containerRef - Ref to the element that portals should be relocated into.
89
+ * The element referenced here, or one of its ancestors,
90
+ * should establish the desired stacking context (for example
91
+ * by using position and z-index).
92
+ */
93
+ export function useTooltipPortalRelocator(
94
+ containerRef: RefObject< HTMLElement | null > | undefined
95
+ ) {
96
+ useEffect( () => {
97
+ const container = containerRef?.current;
98
+ if ( ! container ) {
99
+ return;
100
+ }
101
+
102
+ // Track nodes relocated by this instance so we can move them back on cleanup.
103
+ const instanceNodes = new Set< Node >();
104
+
105
+ const relocateNode = ( node: Node ) => {
106
+ if ( ! isVisxPortalNode( node ) ) {
107
+ return;
108
+ }
109
+
110
+ // Position the portal at the viewport origin so visx's
111
+ // absolute-positioned tooltip coordinates remain correct.
112
+ // Zero-size with overflow: visible so it doesn't affect layout
113
+ // but tooltip content still renders. pointerEvents: none on the
114
+ // wrapper is intentional — tooltip inner elements manage their own.
115
+ node.classList.add( styles.relocatedPortal );
116
+
117
+ // Remember the focused element before moving the node — relocating
118
+ // a DOM subtree causes the browser to blur any focused descendants.
119
+ const { activeElement } = node.ownerDocument;
120
+ const focusedElement =
121
+ activeElement instanceof HTMLElement && node.contains( activeElement )
122
+ ? activeElement
123
+ : null;
124
+
125
+ // Insert at the start of the container (before header and content).
126
+ container.insertBefore( node, container.firstChild );
127
+ relocatedNodes.add( node );
128
+ instanceNodes.add( node );
129
+
130
+ // Restore focus that was lost due to the DOM move.
131
+ if ( focusedElement ) {
132
+ focusedElement.focus();
133
+ }
134
+ };
135
+
136
+ // Patch document.body.removeChild so visx Portal unmount doesn't throw
137
+ // when it tries to remove a node we already moved out of body.
138
+ installRemoveChildPatch();
139
+
140
+ // Relocate any portals that already exist.
141
+ for ( const child of Array.from( document.body.children ) ) {
142
+ relocateNode( child );
143
+ }
144
+
145
+ // Watch for new portals being appended to body.
146
+ const observer = new MutationObserver( mutations => {
147
+ for ( const mutation of mutations ) {
148
+ for ( const node of mutation.addedNodes ) {
149
+ relocateNode( node );
150
+ }
151
+ }
152
+ } );
153
+
154
+ observer.observe( document.body, { childList: true } );
155
+
156
+ return () => {
157
+ // Disconnect first to avoid the observer re-relocating nodes
158
+ // as we move them back to body.
159
+ observer.disconnect();
160
+
161
+ // Move relocated nodes back to body so visx can clean them up
162
+ // normally with the original removeChild.
163
+ for ( const node of instanceNodes ) {
164
+ if ( node instanceof HTMLElement ) {
165
+ node.classList.remove( styles.relocatedPortal );
166
+ }
167
+ if ( node.parentNode === container ) {
168
+ document.body.appendChild( node );
169
+ }
170
+ relocatedNodes.delete( node );
171
+ }
172
+ instanceNodes.clear();
173
+
174
+ uninstallRemoveChildPatch();
175
+ };
176
+ }, [ containerRef ] );
177
+ }
@@ -8,6 +8,7 @@ import {
8
8
  useLayoutEffect,
9
9
  useRef,
10
10
  } from 'react';
11
+ import { useTooltipPortalRelocator } from '../../hooks/use-tooltip-portal-relocator';
11
12
  import {
12
13
  getItemShapeStyles,
13
14
  getSeriesLineStyles,
@@ -26,9 +27,22 @@ export const GlobalChartsContext = createContext< GlobalChartsContextValue | nul
26
27
  export interface GlobalChartsProviderProps {
27
28
  children: ReactNode;
28
29
  theme?: Partial< ChartTheme >;
30
+ /**
31
+ * Optional ref to an element that chart tooltip portals should be relocated into.
32
+ * When provided, visx tooltip portals (normally appended to document.body) will be
33
+ * moved into this container so they participate in the same effective CSS stacking context.
34
+ * The element referenced here, or one of its ancestors, should establish the desired
35
+ * stacking context (for example by using `position` and `z-index`) so that tooltips
36
+ * appear above the relevant chart content.
37
+ */
38
+ portalContainer?: React.RefObject< HTMLElement | null >;
29
39
  }
30
40
 
31
- export const GlobalChartsProvider: FC< GlobalChartsProviderProps > = ( { children, theme } ) => {
41
+ export const GlobalChartsProvider: FC< GlobalChartsProviderProps > = ( {
42
+ children,
43
+ theme,
44
+ portalContainer,
45
+ } ) => {
32
46
  const [ charts, setCharts ] = useState< Map< string, ChartRegistration > >( () => new Map() );
33
47
  // Track hidden series per chart: chartId -> Set<seriesLabel>
34
48
  const [ hiddenSeries, setHiddenSeries ] = useState< Map< string, Set< string > > >(
@@ -38,6 +52,9 @@ export const GlobalChartsProvider: FC< GlobalChartsProviderProps > = ( { childre
38
52
  // Ref to the wrapper element for resolving scoped CSS variables
39
53
  const wrapperRef = useRef< HTMLDivElement >( null );
40
54
 
55
+ // Relocate tooltip portals into the wrapper (or a consumer-provided container) for z-index control.
56
+ useTooltipPortalRelocator( portalContainer ?? wrapperRef );
57
+
41
58
  const providerTheme: CompleteChartTheme = useMemo( () => {
42
59
  return theme ? mergeThemes( defaultTheme, theme ) : defaultTheme;
43
60
  }, [ theme ] );
package/tsup.config.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import babel from 'esbuild-plugin-babel';
1
2
  import { sassPlugin, postcssModules } from 'esbuild-sass-plugin';
2
3
  import { defineConfig } from 'tsup';
3
4
  import pkg from './package.json';
@@ -23,6 +24,16 @@ export default defineConfig( {
23
24
  '.png': 'file',
24
25
  },
25
26
  esbuildPlugins: [
27
+ babel( {
28
+ filter: /\.tsx$/,
29
+ config: {
30
+ presets: [
31
+ '@babel/preset-typescript',
32
+ [ '@babel/preset-react', { runtime: 'automatic' } ],
33
+ ],
34
+ plugins: [ [ 'react-remove-properties', { properties: [ 'data-testid' ] } ] ],
35
+ },
36
+ } ),
26
37
  sassPlugin( {
27
38
  filter: /\.module\.(css|scss)$/,
28
39
  embedded: true,
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/charts/bar-chart/bar-chart.tsx","../src/charts/bar-chart/bar-chart.module.scss","../src/charts/bar-chart/private/use-bar-chart-options.ts","../src/charts/bar-chart/private/truncated-tick-component.tsx"],"sourcesContent":["import { formatNumber } from '@automattic/number-formatters';\nimport { PatternLines, PatternCircles, PatternWaves, PatternHexagons } from '@visx/pattern';\nimport { Axis, BarSeries, BarGroup, Grid, XYChart } from '@visx/xychart';\nimport { __ } from '@wordpress/i18n';\nimport { Stack } from '@wordpress/ui';\nimport clsx from 'clsx';\nimport { useCallback, useContext, useState, useRef, useMemo } from 'react';\nimport { Legend, useChartLegendItems } from '../../components/legend';\nimport { AccessibleTooltip, useKeyboardNavigation } from '../../components/tooltip';\nimport {\n\tuseXYChartTheme,\n\tuseChartDataTransform,\n\tuseZeroValueDisplay,\n\tuseChartMargin,\n\tuseElementHeight,\n\tuseHasLegendChild,\n\tusePrefersReducedMotion,\n} from '../../hooks';\nimport {\n\tGlobalChartsProvider,\n\tuseChartId,\n\tuseChartRegistration,\n\tuseGlobalChartsContext,\n\tuseGlobalChartsTheme,\n\tGlobalChartsContext,\n} from '../../providers';\nimport { attachSubComponents } from '../../utils';\nimport { SingleChartContext } from '../private/single-chart-context';\nimport { withResponsive } from '../private/with-responsive';\nimport styles from './bar-chart.module.scss';\nimport { useBarChartOptions } from './private';\nimport type { BaseChartProps, DataPointDate, SeriesData, Optional } from '../../types';\nimport type { ResponsiveConfig } from '../private/with-responsive';\nimport type { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';\nimport type { GapSize } from '@wordpress/theme';\nimport type { FC, ReactNode, ComponentType } from 'react';\n\nexport interface BarChartProps extends BaseChartProps< SeriesData[] > {\n\trenderTooltip?: ( params: RenderTooltipParams< DataPointDate > ) => ReactNode;\n\torientation?: 'horizontal' | 'vertical';\n\twithPatterns?: boolean;\n\tshowZeroValues?: boolean;\n\tlegendInteractive?: boolean;\n\tchildren?: ReactNode;\n\t/**\n\t * Gap between chart elements (SVG, legend, children).\n\t * Uses WordPress design system tokens.\n\t * @default 'md'\n\t */\n\tgap?: GapSize;\n}\n\n// Base props type with optional responsive properties\ntype BarChartBaseProps = Optional< BarChartProps, 'width' | 'height' | 'size' >;\n\n// Composition API types\ninterface BarChartSubComponents {\n\tLegend: ComponentType< React.ComponentProps< typeof Legend > >;\n}\n\ntype BarChartComponent = FC< BarChartBaseProps > & BarChartSubComponents;\ntype BarChartResponsiveComponent = FC< BarChartBaseProps & ResponsiveConfig > &\n\tBarChartSubComponents;\n\n// Validation function similar to LineChart\nconst validateData = ( data: SeriesData[] ) => {\n\tif ( ! data?.length ) return 'No data available';\n\n\tconst hasInvalidData = data.some( series =>\n\t\tseries.data.some(\n\t\t\tpoint =>\n\t\t\t\tisNaN( point.value as number ) ||\n\t\t\t\tpoint.value === null ||\n\t\t\t\tpoint.value === undefined ||\n\t\t\t\t( ! point.label &&\n\t\t\t\t\t( ! ( 'date' in point && point.date ) || isNaN( point.date.getTime() ) ) )\n\t\t)\n\t);\n\n\tif ( hasInvalidData ) return 'Invalid data';\n\treturn null;\n};\n\nconst getPatternId = ( chartId: string, index: number ) => `bar-pattern-${ chartId }-${ index }`;\n\nconst BarChartInternal: FC< BarChartProps > = ( {\n\tdata,\n\tchartId: providedChartId,\n\twidth,\n\theight,\n\tclassName,\n\tmargin,\n\twithTooltips = false,\n\tshowLegend = false,\n\tlegendOrientation = 'horizontal',\n\tlegendPosition = 'bottom',\n\tlegendAlignment = 'center',\n\tlegendMaxWidth,\n\tlegendTextOverflow = 'wrap',\n\tlegendItemClassName,\n\tlegendShape = 'rect',\n\tgridVisibility: gridVisibilityProp,\n\trenderTooltip,\n\toptions = {},\n\torientation = 'vertical',\n\twithPatterns = false,\n\tshowZeroValues = false,\n\tlegendInteractive = false,\n\tanimation,\n\tchildren,\n\tgap = 'md',\n} ) => {\n\tconst horizontal = orientation === 'horizontal';\n\tconst chartId = useChartId( providedChartId );\n\tconst theme = useXYChartTheme( data );\n\n\tconst dataSorted = useChartDataTransform( data );\n\n\t// Transform data to add a small value for zero bars to make them visible\n\tconst dataWithVisibleZeros = useZeroValueDisplay( dataSorted, {\n\t\tenabled: showZeroValues,\n\t} );\n\n\t// Create legend items using the reusable hook\n\tconst legendItems = useChartLegendItems( dataSorted );\n\tconst chartOptions = useBarChartOptions( dataWithVisibleZeros, horizontal, options );\n\tconst defaultMargin = useChartMargin( height, chartOptions, dataSorted, theme, horizontal );\n\tconst [ svgWrapperRef, svgWrapperHeight ] = useElementHeight< HTMLDivElement >();\n\tconst chartRef = useRef< HTMLDivElement >( null );\n\n\t// Check if children contain a Legend component (composition pattern)\n\tconst hasLegendChild = useHasLegendChild( children );\n\n\t// Use the measured SVG wrapper height, falling back to the passed height if provided.\n\t// When there's a legend (via prop or composition), we must wait for measurement because\n\t// the legend takes space and the svg-wrapper height will be less than the total height.\n\tconst chartHeight = svgWrapperHeight > 0 ? svgWrapperHeight : height;\n\tconst hasLegend = showLegend || hasLegendChild;\n\tconst isWaitingForMeasurement = hasLegend ? svgWrapperHeight === 0 : ! chartHeight;\n\tconst [ selectedIndex, setSelectedIndex ] = useState< number | undefined >( undefined );\n\tconst [ isNavigating, setIsNavigating ] = useState( false );\n\n\tconst totalPoints =\n\t\tMath.max( 0, ...data.map( series => series.data?.length || 0 ) ) * data.length;\n\n\t// Use the keyboard navigation hook\n\tconst { tooltipRef, onChartFocus, onChartBlur, onChartKeyDown } = useKeyboardNavigation( {\n\t\tselectedIndex,\n\t\tsetSelectedIndex,\n\t\tisNavigating,\n\t\tsetIsNavigating,\n\t\tchartRef,\n\t\ttotalPoints,\n\t} );\n\n\tconst { getElementStyles, isSeriesVisible } = useGlobalChartsContext();\n\tconst providerTheme = useGlobalChartsTheme();\n\n\t// Add visibility information to series when using interactive legends\n\tconst seriesWithVisibility = useMemo( () => {\n\t\tif ( ! chartId || ! legendInteractive ) {\n\t\t\treturn dataWithVisibleZeros.map( ( series, index ) => ( {\n\t\t\t\tseries,\n\t\t\t\tindex,\n\t\t\t\tisVisible: true,\n\t\t\t} ) );\n\t\t}\n\t\treturn dataWithVisibleZeros.map( ( series, index ) => ( {\n\t\t\tseries,\n\t\t\tindex,\n\t\t\tisVisible: isSeriesVisible( chartId, series.label ),\n\t\t} ) );\n\t}, [ dataWithVisibleZeros, chartId, isSeriesVisible, legendInteractive ] );\n\n\t// Check if all series are hidden\n\tconst allSeriesHidden = useMemo( () => {\n\t\treturn seriesWithVisibility.every( ( { isVisible } ) => ! isVisible );\n\t}, [ seriesWithVisibility ] );\n\n\tconst getBarBackground = useCallback(\n\t\t( index: number ) => () =>\n\t\t\twithPatterns\n\t\t\t\t? `url(#${ getPatternId( chartId, index ) })`\n\t\t\t\t: getElementStyles( { data: dataSorted[ index ], index } ).color,\n\t\t[ withPatterns, getElementStyles, dataSorted, chartId ]\n\t);\n\n\tconst renderDefaultTooltip = useCallback(\n\t\t( { tooltipData }: RenderTooltipParams< DataPointDate > ) => {\n\t\t\tconst nearestDatum = tooltipData?.nearestDatum?.datum;\n\t\t\tif ( ! nearestDatum ) return null;\n\n\t\t\treturn (\n\t\t\t\t<div className={ styles[ 'bar-chart__tooltip' ] }>\n\t\t\t\t\t<div className={ styles[ 'bar-chart__tooltip-header' ] }>\n\t\t\t\t\t\t{ tooltipData?.nearestDatum?.key }\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className={ styles[ 'bar-chart__tooltip-row' ] }>\n\t\t\t\t\t\t<span className={ styles[ 'bar-chart__tooltip-label' ] }>\n\t\t\t\t\t\t\t{ chartOptions.tooltip.labelFormatter(\n\t\t\t\t\t\t\t\tnearestDatum.label || ( nearestDatum.date ? nearestDatum.date.getTime() : 0 ),\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t[]\n\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t:\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className={ styles[ 'bar-chart__tooltip-value' ] }>\n\t\t\t\t\t\t\t{ formatNumber( nearestDatum.value as number ) }\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t);\n\t\t},\n\t\t[ chartOptions.tooltip ]\n\t);\n\n\tconst renderPattern = useCallback(\n\t\t( index: number, color: string ) => {\n\t\t\tconst patternType = index % 4;\n\t\t\tconst id = getPatternId( chartId, index );\n\t\t\tconst commonProps = {\n\t\t\t\tid,\n\t\t\t\tstroke: 'white',\n\t\t\t\tstrokeWidth: 1,\n\t\t\t\tbackground: color,\n\t\t\t};\n\n\t\t\tswitch ( patternType ) {\n\t\t\t\tcase 0:\n\t\t\t\tdefault:\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<PatternLines\n\t\t\t\t\t\t\tkey={ id }\n\t\t\t\t\t\t\t{ ...commonProps }\n\t\t\t\t\t\t\twidth={ 5 }\n\t\t\t\t\t\t\theight={ 5 }\n\t\t\t\t\t\t\torientation={ [ 'diagonal' ] }\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\tcase 1:\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<PatternCircles key={ id } { ...commonProps } width={ 6 } height={ 6 } fill=\"white\" />\n\t\t\t\t\t);\n\t\t\t\tcase 2:\n\t\t\t\t\treturn <PatternWaves key={ id } { ...commonProps } width={ 4 } height={ 4 } />;\n\t\t\t\tcase 3:\n\t\t\t\t\treturn <PatternHexagons key={ id } { ...commonProps } size={ 8 } height={ 3 } />;\n\t\t\t}\n\t\t},\n\t\t[ chartId ]\n\t);\n\n\tconst createPatternBorderStyle = useCallback(\n\t\t( index: number, color: string ) => {\n\t\t\tconst patternId = getPatternId( chartId, index );\n\t\t\treturn `\n\t\t\t.visx-bar[fill=\"url(#${ patternId })\"] {\n\t\t\t\tstroke: ${ color };\n\t\t\t\tstroke-width: 1;\n\t\t\t\t}\n\t\t\t`;\n\t\t},\n\t\t[ chartId ]\n\t);\n\n\tconst createKeyboardHighlightStyle = useCallback( () => {\n\t\tif ( selectedIndex === undefined ) return '';\n\n\t\t// Calculate which bar should be highlighted based on selectedIndex\n\t\t// Pattern: [series1[0], series2[0], series3[0], series1[1], series2[1], series3[1], ...]\n\t\tconst maxDataPoints = Math.max( ...data.map( s => s.data.length ) );\n\t\tconst dataPointIndex = Math.floor( selectedIndex / data.length );\n\t\tconst seriesIndex = selectedIndex % data.length;\n\n\t\t// Only highlight if we're within valid bounds\n\t\tif ( dataPointIndex >= maxDataPoints || seriesIndex >= data.length ) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst seriesData = data[ seriesIndex ];\n\t\tif ( dataPointIndex >= seriesData.data.length ) {\n\t\t\treturn '';\n\t\t}\n\n\t\t// Based on the DOM structure analysis:\n\t\t// - All bars are in a single .visx-bar-group\n\t\t// - Bars are ordered as: [series1[0], series1[1], series2[0], series2[1], ...]\n\t\t// - So we need to calculate the actual bar index in the DOM\n\t\tconst actualBarIndex = seriesIndex * maxDataPoints + dataPointIndex;\n\n\t\t// Use a CSS class selector instead of ID since useId() generates invalid CSS ID characters\n\t\tconst generatedStyles = `\n\t\t\t.bar-chart[data-chart-id=\"bar-chart-${ chartId }\"] .visx-bar-group .visx-bar:nth-child(${\n\t\t\t\tactualBarIndex + 1\n\t\t\t}) {\n\t\t\t\tstroke: #005fcc;\n\t\t\t\tstroke-width: 2px;\n\t\t\t}\n\t\t`;\n\n\t\treturn generatedStyles;\n\t}, [ selectedIndex, data, chartId ] );\n\n\t// Validate data first\n\tconst error = validateData( dataSorted );\n\tconst isDataValid = ! error;\n\n\t// Memoize metadata to prevent unnecessary re-registration\n\tconst chartMetadata = useMemo(\n\t\t() => ( {\n\t\t\torientation,\n\t\t\twithPatterns,\n\t\t} ),\n\t\t[ orientation, withPatterns ]\n\t);\n\n\t// Register chart with context only if data is valid\n\tuseChartRegistration( {\n\t\tchartId,\n\t\tlegendItems,\n\t\tchartType: 'bar',\n\t\tisDataValid,\n\t\tmetadata: chartMetadata,\n\t} );\n\n\tconst prefersReducedMotion = usePrefersReducedMotion();\n\n\tif ( error ) {\n\t\treturn <div className={ clsx( 'bar-chart', styles[ 'bar-chart' ] ) }>{ error }</div>;\n\t}\n\n\tconst gridVisibility = gridVisibilityProp ?? chartOptions.gridVisibility;\n\tconst highlightedBarStyle = createKeyboardHighlightStyle();\n\n\tconst legendElement = showLegend && (\n\t\t<Legend\n\t\t\torientation={ legendOrientation }\n\t\t\tposition={ legendPosition }\n\t\t\talignment={ legendAlignment }\n\t\t\tmaxWidth={ legendMaxWidth }\n\t\t\ttextOverflow={ legendTextOverflow }\n\t\t\tlegendItemClassName={ legendItemClassName }\n\t\t\tclassName={ styles[ 'bar-chart__legend' ] }\n\t\t\tshape={ legendShape }\n\t\t\tchartId={ chartId }\n\t\t\tinteractive={ legendInteractive }\n\t\t/>\n\t);\n\n\treturn (\n\t\t<SingleChartContext.Provider\n\t\t\tvalue={ {\n\t\t\t\tchartId,\n\t\t\t\tchartWidth: width,\n\t\t\t\tchartHeight,\n\t\t\t} }\n\t\t>\n\t\t\t<Stack\n\t\t\t\tdirection=\"column\"\n\t\t\t\tgap={ gap }\n\t\t\t\tclassName={ clsx(\n\t\t\t\t\t'bar-chart',\n\t\t\t\t\tstyles[ 'bar-chart' ],\n\t\t\t\t\t{\n\t\t\t\t\t\t[ styles[ `bar-chart--animated${ horizontal ? '-horizontal' : '' }` ] ]:\n\t\t\t\t\t\t\tanimation && ! prefersReducedMotion,\n\t\t\t\t\t},\n\t\t\t\t\tclassName\n\t\t\t\t) }\n\t\t\t\tdata-testid=\"bar-chart\"\n\t\t\t\tstyle={ {\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t\tvisibility: isWaitingForMeasurement ? 'hidden' : 'visible',\n\t\t\t\t} }\n\t\t\t\tdata-chart-id={ `bar-chart-${ chartId }` }\n\t\t\t>\n\t\t\t\t{ legendPosition === 'top' && legendElement }\n\n\t\t\t\t<div\n\t\t\t\t\tclassName={ styles[ 'bar-chart__svg-wrapper' ] }\n\t\t\t\t\tref={ svgWrapperRef }\n\t\t\t\t\trole=\"grid\"\n\t\t\t\t\taria-label={ __( 'Bar chart', 'jetpack-charts' ) }\n\t\t\t\t\ttabIndex={ 0 }\n\t\t\t\t\tonKeyDown={ onChartKeyDown }\n\t\t\t\t\tonFocus={ onChartFocus }\n\t\t\t\t\tonBlur={ onChartBlur }\n\t\t\t\t>\n\t\t\t\t\t{ ! isWaitingForMeasurement && (\n\t\t\t\t\t\t<div ref={ chartRef }>\n\t\t\t\t\t\t\t<XYChart\n\t\t\t\t\t\t\t\ttheme={ theme }\n\t\t\t\t\t\t\t\twidth={ width }\n\t\t\t\t\t\t\t\theight={ chartHeight }\n\t\t\t\t\t\t\t\tmargin={ {\n\t\t\t\t\t\t\t\t\t...defaultMargin,\n\t\t\t\t\t\t\t\t\t...margin,\n\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t\txScale={ chartOptions.xScale }\n\t\t\t\t\t\t\t\tyScale={ chartOptions.yScale }\n\t\t\t\t\t\t\t\thorizontal={ horizontal }\n\t\t\t\t\t\t\t\tpointerEventsDataKey=\"nearest\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Grid\n\t\t\t\t\t\t\t\t\tcolumns={ gridVisibility.includes( 'y' ) }\n\t\t\t\t\t\t\t\t\trows={ gridVisibility.includes( 'x' ) }\n\t\t\t\t\t\t\t\t\tnumTicks={ 4 }\n\t\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t\t{ withPatterns && (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<defs data-testid=\"bar-chart-patterns\">\n\t\t\t\t\t\t\t\t\t\t\t{ dataSorted.map( ( seriesData, index ) =>\n\t\t\t\t\t\t\t\t\t\t\t\trenderPattern(\n\t\t\t\t\t\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tgetElementStyles( { data: seriesData, index } ).color\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t\t\t</defs>\n\t\t\t\t\t\t\t\t\t\t<style>\n\t\t\t\t\t\t\t\t\t\t\t{ dataSorted.map( ( seriesData, index ) =>\n\t\t\t\t\t\t\t\t\t\t\t\tcreatePatternBorderStyle(\n\t\t\t\t\t\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tgetElementStyles( { data: seriesData, index } ).color\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t\t\t</style>\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t) }\n\n\t\t\t\t\t\t\t\t{ highlightedBarStyle && <style>{ highlightedBarStyle }</style> }\n\n\t\t\t\t\t\t\t\t{ allSeriesHidden ? (\n\t\t\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\t\t\tx={ width / 2 }\n\t\t\t\t\t\t\t\t\t\ty={ chartHeight / 2 }\n\t\t\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\t\t\tfill={ providerTheme.gridStyles?.stroke || '#ccc' }\n\t\t\t\t\t\t\t\t\t\tfontSize=\"14\"\n\t\t\t\t\t\t\t\t\t\tfontFamily=\"-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,sans-serif\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{ __(\n\t\t\t\t\t\t\t\t\t\t\t'All series are hidden. Click legend items to show data.',\n\t\t\t\t\t\t\t\t\t\t\t'jetpack-charts'\n\t\t\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t\t\t) : null }\n\n\t\t\t\t\t\t\t\t<BarGroup padding={ chartOptions.barGroup.padding }>\n\t\t\t\t\t\t\t\t\t{ seriesWithVisibility.map( ( { series: seriesData, index, isVisible } ) => {\n\t\t\t\t\t\t\t\t\t\t// Skip rendering invisible series\n\t\t\t\t\t\t\t\t\t\tif ( ! isVisible ) {\n\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t<BarSeries\n\t\t\t\t\t\t\t\t\t\t\t\tkey={ seriesData?.label }\n\t\t\t\t\t\t\t\t\t\t\t\tdataKey={ seriesData?.label }\n\t\t\t\t\t\t\t\t\t\t\t\tdata={ seriesData.data as DataPointDate[] }\n\t\t\t\t\t\t\t\t\t\t\t\tyAccessor={ chartOptions.accessors.yAccessor }\n\t\t\t\t\t\t\t\t\t\t\t\txAccessor={ chartOptions.accessors.xAccessor }\n\t\t\t\t\t\t\t\t\t\t\t\tcolorAccessor={ getBarBackground( index ) }\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} ) }\n\t\t\t\t\t\t\t\t</BarGroup>\n\n\t\t\t\t\t\t\t\t<Axis { ...chartOptions.axis.x } />\n\t\t\t\t\t\t\t\t<Axis { ...chartOptions.axis.y } />\n\n\t\t\t\t\t\t\t\t{ withTooltips && (\n\t\t\t\t\t\t\t\t\t<AccessibleTooltip\n\t\t\t\t\t\t\t\t\t\tdetectBounds\n\t\t\t\t\t\t\t\t\t\tsnapTooltipToDatumX\n\t\t\t\t\t\t\t\t\t\tsnapTooltipToDatumY\n\t\t\t\t\t\t\t\t\t\trenderTooltip={ renderTooltip || renderDefaultTooltip }\n\t\t\t\t\t\t\t\t\t\tselectedIndex={ selectedIndex }\n\t\t\t\t\t\t\t\t\t\ttooltipRef={ tooltipRef }\n\t\t\t\t\t\t\t\t\t\tkeyboardFocusedClassName={ styles[ 'bar-chart__tooltip--keyboard-focused' ] }\n\t\t\t\t\t\t\t\t\t\tseries={ data }\n\t\t\t\t\t\t\t\t\t\tmode=\"individual\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</XYChart>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) }\n\t\t\t\t</div>\n\n\t\t\t\t{ legendPosition === 'bottom' && legendElement }\n\n\t\t\t\t{ children }\n\t\t\t</Stack>\n\t\t</SingleChartContext.Provider>\n\t);\n};\n\nconst BarChartWithProvider: FC< BarChartProps > = props => {\n\tconst existingContext = useContext( GlobalChartsContext );\n\n\t// If we're already in a GlobalChartsProvider context, don't create a new one\n\tif ( existingContext ) {\n\t\treturn <BarChartInternal { ...props } />;\n\t}\n\n\t// Otherwise, create our own GlobalChartsProvider\n\treturn (\n\t\t<GlobalChartsProvider>\n\t\t\t<BarChartInternal { ...props } />\n\t\t</GlobalChartsProvider>\n\t);\n};\n\nBarChartWithProvider.displayName = 'BarChart';\n\n// Create BarChart with composition API\nconst BarChart = attachSubComponents( BarChartWithProvider, {\n\tLegend: Legend,\n} ) as BarChartComponent;\n\n// Create responsive BarChart with composition API\nconst BarChartResponsive = attachSubComponents(\n\twithResponsive< BarChartProps >( BarChartWithProvider ),\n\t{\n\t\tLegend: Legend,\n\t}\n) as BarChartResponsiveComponent;\n\nexport { BarChartResponsive as default, BarChart as BarChartUnresponsive };\n","import 'css-chunk:src/charts/bar-chart/bar-chart.module.scss';export default {\n \"bar-chart__svg-wrapper\": \"a8ccharts-9CsqC0\",\n \"bar-chart\": \"a8ccharts-3gflnB\",\n \"bar-chart--animated\": \"a8ccharts-98W-yu\",\n \"rise\": \"a8ccharts-z6AsiQ\",\n \"bar-chart--animated-horizontal\": \"a8ccharts-HFA3FF\",\n \"stretch\": \"a8ccharts-DQp37O\"\n};","import { formatNumberCompact } from '@automattic/number-formatters';\nimport { useMemo } from 'react';\nimport { TruncatedXTickComponent, TruncatedYTickComponent } from './truncated-tick-component';\nimport type { EnhancedDataPoint } from '../../../hooks/use-zero-value-display';\nimport type { DataPointDate, BaseChartProps, SeriesData } from '../../../types';\nimport type { TickFormatter } from '@visx/axis';\n\nconst formatDateTick = ( timestamp: number ) => {\n\tconst date = new Date( timestamp );\n\treturn date.toLocaleDateString( undefined, {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t} );\n};\n\n/**\n * Get the group padding of a scale.\n *\n * @param scale - The scale to get the group padding of.\n * @return The group padding of the scale.\n */\nconst getGroupPadding = ( scale: Record< string, unknown > ): number => {\n\treturn typeof scale.paddingInner === 'number' ? ( scale.paddingInner as number ) : 0;\n};\n\n/**\n * Returns the merged options for the bar chart, including axis and scale configuration based on the orientation.\n *\n * @param data - The data to be displayed in the chart.\n * @param horizontal - Whether the chart is horizontal or vertical.\n * @param options - The options for the chart.\n * @return The merged options for the chart.\n */\nexport function useBarChartOptions(\n\tdata: SeriesData[],\n\thorizontal: boolean,\n\toptions: BaseChartProps[ 'options' ] = {}\n) {\n\tconst defaultOptions = useMemo( () => {\n\t\tconst bandScale = {\n\t\t\ttype: 'band' as const,\n\t\t\tpadding: 0.2,\n\t\t\tpaddingInner: 0.1,\n\t\t};\n\t\tconst linearScale = {\n\t\t\ttype: 'linear' as const,\n\t\t\tnice: true,\n\t\t\tzero: false,\n\t\t};\n\n\t\tconst labelFormatter = data?.[ 0 ]?.data?.[ 0 ]?.label\n\t\t\t? ( label: string ) => label\n\t\t\t: formatDateTick;\n\t\tconst valueFormatter = formatNumberCompact as TickFormatter< unknown >;\n\n\t\tconst labelAccessor = ( d: DataPointDate ) => d?.label || d?.date;\n\t\tconst valueAccessor = ( d: DataPointDate | EnhancedDataPoint ) => {\n\t\t\t// Use visualValue for bar rendering if available (for zero values), otherwise use value\n\t\t\tconst enhancedPoint = d as EnhancedDataPoint;\n\t\t\treturn enhancedPoint?.visualValue !== undefined ? enhancedPoint.visualValue : d?.value;\n\t\t};\n\n\t\treturn {\n\t\t\tvertical: {\n\t\t\t\txTickFormat: labelFormatter,\n\t\t\t\tyTickFormat: valueFormatter,\n\t\t\t\ttooltipLabelFormatter: labelFormatter,\n\t\t\t\txAccessor: labelAccessor,\n\t\t\t\tyAccessor: valueAccessor,\n\t\t\t\tgridVisibility: 'x',\n\t\t\t\txScale: bandScale,\n\t\t\t\tyScale: linearScale,\n\t\t\t},\n\t\t\thorizontal: {\n\t\t\t\txTickFormat: valueFormatter,\n\t\t\t\tyTickFormat: labelFormatter,\n\t\t\t\ttooltipLabelFormatter: labelFormatter,\n\t\t\t\txAccessor: valueAccessor,\n\t\t\t\tyAccessor: labelAccessor,\n\t\t\t\tgridVisibility: 'y',\n\t\t\t\txScale: linearScale,\n\t\t\t\tyScale: bandScale,\n\t\t\t},\n\t\t};\n\t}, [ data ] );\n\n\treturn useMemo( () => {\n\t\tconst orientationKey = horizontal ? 'horizontal' : 'vertical';\n\t\tconst {\n\t\t\txTickFormat,\n\t\t\tyTickFormat,\n\t\t\ttooltipLabelFormatter: defaultTooltipLabelFormatter,\n\t\t\txAccessor,\n\t\t\tyAccessor,\n\t\t\tgridVisibility,\n\t\t\txScale: baseXScale,\n\t\t\tyScale: baseYScale,\n\t\t} = defaultOptions[ orientationKey ];\n\n\t\tconst xScale = { ...baseXScale, ...( options.xScale || {} ) };\n\t\tconst yScale = { ...baseYScale, ...( options.yScale || {} ) };\n\t\tconst providedToolTipLabelFormatter = horizontal\n\t\t\t? options.axis?.y?.tickFormat\n\t\t\t: options.axis?.x?.tickFormat;\n\n\t\tconst { labelOverflow: xLabelOverflow, ...xAxisOptions } = options.axis?.x || {};\n\t\tconst { labelOverflow: yLabelOverflow, ...yAxisOptions } = options.axis?.y || {};\n\n\t\treturn {\n\t\t\tgridVisibility,\n\t\t\txScale,\n\t\t\tyScale,\n\t\t\taccessors: {\n\t\t\t\txAccessor,\n\t\t\t\tyAccessor,\n\t\t\t},\n\t\t\taxis: {\n\t\t\t\tx: {\n\t\t\t\t\torientation: 'bottom' as const,\n\t\t\t\t\tnumTicks: 4,\n\t\t\t\t\ttickFormat: xTickFormat,\n\t\t\t\t\t...( xLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedXTickComponent } : {} ),\n\t\t\t\t\t...xAxisOptions,\n\t\t\t\t},\n\t\t\t\ty: {\n\t\t\t\t\torientation: 'left' as const,\n\t\t\t\t\tnumTicks: 4,\n\t\t\t\t\ttickFormat: yTickFormat,\n\t\t\t\t\t...( yLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedYTickComponent } : {} ),\n\t\t\t\t\t...yAxisOptions,\n\t\t\t\t},\n\t\t\t},\n\t\t\tbarGroup: {\n\t\t\t\tpadding: getGroupPadding( horizontal ? yScale : xScale ),\n\t\t\t},\n\t\t\ttooltip: {\n\t\t\t\tlabelFormatter: providedToolTipLabelFormatter || defaultTooltipLabelFormatter,\n\t\t\t},\n\t\t};\n\t}, [ defaultOptions, options, horizontal ] );\n}\n","import { DataContext } from '@visx/xychart';\nimport { useContext } from 'react';\nimport { isSafari } from '../../../utils';\nimport type { AxisScale, TickRendererProps } from '@visx/axis';\nimport type { FC, CSSProperties } from 'react';\n\n/**\n * Get the bandwidth of a scale\n *\n * @param scale - The scale to get the bandwidth of\n * @return The bandwidth of the scale\n */\nconst getScaleBandwidth = < Scale extends AxisScale >( scale?: Scale ) => {\n\treturn scale && 'bandwidth' in scale ? scale.bandwidth() ?? 0 : 0;\n};\ninterface TruncatedTickComponentProps extends TickRendererProps {\n\t/** Which axis this tick belongs to */\n\taxis: 'x' | 'y';\n}\n\n/**\n * Minimum width in pixels for tick labels when scale bandwidth is very small.\n * Prevents labels from collapsing to unreadable widths on dense charts.\n *\n * Trade-off: When bandwidth is less than this minimum (e.g., many bars in a narrow chart),\n * adjacent labels may overlap since each label uses this minimum width regardless of\n * available space. This prioritizes label readability over preventing overlap.\n *\n * For very dense charts where overlap occurs, consider:\n * - Using `numTicks` option to reduce the number of displayed labels\n * - Using `tickFormat` to abbreviate label text\n * - Increasing chart width or reducing data points\n */\nconst MIN_TICK_LABEL_WIDTH = 20;\n\n/**\n * A tick component that renders labels with text truncation (ellipsis) when they exceed\n * the available bandwidth. Shows the full text on hover via native title attribute.\n *\n * Uses foreignObject to embed HTML within SVG, enabling CSS text-overflow: ellipsis.\n * Inherits text styles from tickLabelProps passed by visx Axis component.\n *\n * Note: A minimum label width (MIN_TICK_LABEL_WIDTH) is enforced to keep labels readable.\n * On very dense charts where bandwidth < 20px, this may cause label overlap.\n * See MIN_TICK_LABEL_WIDTH documentation for mitigation strategies.\n *\n * @param props - The props for the truncated tick component\n * @param props.x - The x position of the tick\n * @param props.y - The y position of the tick\n * @param props.formattedValue - The formatted value of the tick\n * @param props.axis - The axis this tick belongs to\n * @param props.textAnchor - The text anchor of the tick\n * @param props.fill - The fill color of the tick\n * @param props.dy - The dy offset of the tick\n *\n * @return The truncated tick component\n */\nexport const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( {\n\tx,\n\ty,\n\tformattedValue,\n\taxis,\n\ttextAnchor,\n\tfill,\n\tdy,\n\t...textProps\n} ) => {\n\t// Get max width of the tick label\n\tconst { xScale, yScale } = useContext( DataContext ) || {};\n\tconst scale = axis === 'x' ? xScale : yScale;\n\tconst bandwidth = getScaleBandwidth( scale );\n\tconst maxWidth = Math.max( bandwidth, MIN_TICK_LABEL_WIDTH );\n\n\t// Map SVG textAnchor to CSS textAlign\n\tlet textAlign: 'left' | 'right' | 'center' = 'center';\n\tif ( textAnchor === 'start' ) {\n\t\ttextAlign = 'left';\n\t} else if ( textAnchor === 'end' ) {\n\t\ttextAlign = 'right';\n\t} else if ( textAnchor === 'middle' ) {\n\t\ttextAlign = 'center';\n\t}\n\n\t// Calculate x offset based on text alignment\n\tlet xOffset = 0;\n\tif ( textAlign === 'center' ) {\n\t\txOffset = -maxWidth / 2;\n\t} else if ( textAlign === 'right' ) {\n\t\txOffset = -maxWidth;\n\t}\n\n\t// Extract compatible style properties from SVG text props\n\tconst { fontSize, fontFamily, fontWeight, fontStyle, letterSpacing, opacity } = textProps as {\n\t\tfontSize?: CSSProperties[ 'fontSize' ];\n\t\tfontFamily?: CSSProperties[ 'fontFamily' ];\n\t\tfontWeight?: CSSProperties[ 'fontWeight' ];\n\t\tfontStyle?: CSSProperties[ 'fontStyle' ];\n\t\tletterSpacing?: CSSProperties[ 'letterSpacing' ];\n\t\topacity?: CSSProperties[ 'opacity' ];\n\t};\n\n\tconst textStyles: CSSProperties = {\n\t\t/**\n\t\t * SVG <text> elements are vertically aligned to the baseline by default, but HTML <div> elements inside <foreignObject>\n\t\t * are positioned relative to the top-left corner. To visually align the tick label like SVG text,\n\t\t * we shift the div up by 100% of its height and adjust by twice the SVG dy value (from visx) to approximate original placement.\n\t\t */\n\t\ttransform: `translateY(calc(-100% + ${ dy ?? '0' } * 2))`,\n\t\t// Safari doesn't work well with foreignObject positioning. Use position: fixed as a workaround.\n\t\t...( isSafari() ? { position: 'fixed' as const } : {} ),\n\t\t// Apply compatible SVG text styles\n\t\tfontSize,\n\t\tfontFamily,\n\t\tfontWeight,\n\t\tfontStyle,\n\t\tletterSpacing,\n\t\topacity,\n\t\t// Convert svg text styles to CSS styles for the div\n\t\tcolor: fill ?? 'inherit',\n\t\ttextAlign,\n\t\t// Ensure text is truncated with ellipsis, remains on one line, and shows the full value in a tooltip on hover.\n\t\t// The surrounding div uses CSS to handle overflow, and the 'title' attribute is set for accessibility.\n\t\twidth: maxWidth,\n\t\toverflow: 'hidden',\n\t\ttextOverflow: 'ellipsis',\n\t\twhiteSpace: 'nowrap',\n\t\tcursor: 'default',\n\t\tpointerEvents: 'auto',\n\t};\n\n\treturn (\n\t\t<foreignObject x={ x + xOffset } y={ y } width={ maxWidth } height={ 0 } overflow=\"visible\">\n\t\t\t<div style={ textStyles } title={ formattedValue }>\n\t\t\t\t{ formattedValue }\n\t\t\t</div>\n\t\t</foreignObject>\n\t);\n};\n\n/**\n * Factory function to create a truncated tick component for a specific axis.\n * Returns a component that can be passed to visx's tickComponent prop.\n *\n * @param axis - The axis this tick component is for ('x' or 'y')\n * @return A tick component function compatible with visx's TickRendererProps\n */\nconst createTruncatedTickComponent = ( axis: 'x' | 'y' ) => ( props: TickRendererProps ) => {\n\treturn <TruncatedTickComponent { ...props } axis={ axis } />;\n};\n\n/**\n * Pre-created tick components for x and y axes.\n * These functions are created once at module initialization and reused,\n * avoiding repeated factory calls when configuring axes.\n */\nexport const TruncatedXTickComponent = createTruncatedTickComponent( 'x' );\nexport const TruncatedYTickComponent = createTruncatedTickComponent( 'y' );\n"],"mappings":"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","names":["useContext","useMemo","jsx","useMemo","useContext"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/tooltip/base-tooltip.tsx","../src/components/tooltip/base-tooltip.module.scss","../src/components/tooltip/accessible-tooltip.tsx"],"sourcesContent":["import { formatNumber } from '@automattic/number-formatters';\nimport styles from './base-tooltip.module.scss';\nimport type { CSSProperties, ComponentType, ReactNode } from 'react';\n\ntype TooltipData = {\n\tlabel: string;\n\tvalue: number;\n\tvalueDisplay?: string;\n};\n\ntype TooltipComponentProps = {\n\tdata: TooltipData;\n\tclassName?: string;\n};\n\ntype TooltipCommonProps = {\n\ttop: number;\n\tleft: number;\n\tstyle?: CSSProperties;\n\tclassName?: string;\n\t/**\n\t * Whether to render the tooltip container div. When false, only renders the content.\n\t * Useful when the tooltip is rendered inside a portal or custom container.\n\t * @default true\n\t */\n\trenderContainer?: boolean;\n};\n\ntype DefaultDataTooltip = {\n\tdata: TooltipData;\n\tcomponent?: ComponentType< TooltipComponentProps >;\n\tchildren?: never;\n};\n\ntype CustomTooltip = {\n\tchildren: ReactNode;\n\tdata?: never;\n\tcomponent?: never;\n};\n\ntype BaseTooltipProps = TooltipCommonProps & ( DefaultDataTooltip | CustomTooltip );\n\nconst DefaultTooltipContent = ( { data }: TooltipComponentProps ) => (\n\t<>\n\t\t{ data?.label }: { data?.valueDisplay || formatNumber( data?.value ) }\n\t</>\n);\n\nexport const BaseTooltip = ( {\n\tdata,\n\ttop,\n\tleft,\n\tcomponent: Component = DefaultTooltipContent,\n\tchildren,\n\tclassName,\n\tstyle,\n\trenderContainer = true,\n}: BaseTooltipProps ) => {\n\tconst content = children || ( data && <Component data={ data } className={ className } /> );\n\n\tif ( ! renderContainer ) {\n\t\treturn content;\n\t}\n\n\treturn (\n\t\t<div className={ styles.tooltip } style={ { top, left, ...style } } role=\"tooltip\">\n\t\t\t{ content }\n\t\t</div>\n\t);\n};\n\nexport type { BaseTooltipProps, TooltipData };\n","import 'css-chunk:src/components/tooltip/base-tooltip.module.scss';export default {\n \"tooltip\": \"a8ccharts-OfX6nd\"\n};","import { Tooltip, TooltipContext } from '@visx/xychart';\nimport { useContext, useEffect, useCallback, useMemo } from 'react';\nimport type { SeriesData, DataPointDate } from '../../types';\nimport type {\n\tTooltipProps as BaseTooltipProps,\n\tRenderTooltipParams,\n} from '@visx/xychart/lib/components/Tooltip';\nimport type { ReactNode } from 'react';\n\n// Type for flattened tooltip data used in individual mode\nexport type FlattenedTooltipData = {\n\tdatum: DataPointDate;\n\tseriesLabel: string;\n\tseriesIndex: number;\n\tdataPointIndex: number;\n};\n\n// Enhanced tooltip with keyboard navigation and accessibility\ninterface AccessibleTooltipProps\n\textends Omit< BaseTooltipProps< DataPointDate >, 'renderTooltip' > {\n\trenderTooltip?: ( params: RenderTooltipParams< DataPointDate > ) => ReactNode;\n\tselectedIndex?: number | undefined;\n\ttooltipRef?: ( element: HTMLDivElement | null ) => void;\n\tkeyboardFocusedClassName?: string;\n\t/**\n\t * Flattened tooltip data prepared by parent component\n\t * Each index corresponds to one tooltip to show\n\t */\n\ttooltipData?: FlattenedTooltipData[];\n\t/**\n\t * For line charts: series data to show all series at selected data point\n\t * When provided, shows all series instead of individual tooltips\n\t */\n\tseries?: SeriesData[];\n\t/**\n\t * Whether to combine tooltip information from multiple series into a single tooltip. This is useful for line charts.\n\t * Or to show individual tooltips for each series. This is useful for bar charts.\n\t */\n\tmode?: 'individual' | 'group';\n}\n\nexport const AccessibleTooltip: React.FC< AccessibleTooltipProps > = ( {\n\trenderTooltip,\n\tselectedIndex,\n\ttooltipRef,\n\tkeyboardFocusedClassName,\n\tseries = [],\n\tmode = 'group',\n\t...props\n} ) => {\n\tconst tooltipContext = useContext( TooltipContext );\n\n\tconst tooltipData = useMemo( () => {\n\t\tif ( mode !== 'individual' ) return [];\n\t\tif ( series.length === 0 ) return [];\n\n\t\tconst maxDataPoints = Math.max( ...series.map( s => s.data.length ) );\n\t\tconst flattened: Array< {\n\t\t\tdatum: DataPointDate;\n\t\t\tseriesLabel: string;\n\t\t\tseriesIndex: number;\n\t\t\tdataPointIndex: number;\n\t\t} > = [];\n\n\t\t// Pattern: [series1[0], series2[0], series3[0], series1[1], series2[1], series3[1], ...]\n\t\tfor ( let dataPointIndex = 0; dataPointIndex < maxDataPoints; dataPointIndex++ ) {\n\t\t\tfor ( let seriesIndex = 0; seriesIndex < series.length; seriesIndex++ ) {\n\t\t\t\tconst seriesData = series[ seriesIndex ];\n\t\t\t\tif ( dataPointIndex < seriesData.data.length ) {\n\t\t\t\t\tflattened.push( {\n\t\t\t\t\t\tdatum: seriesData.data[ dataPointIndex ] as DataPointDate,\n\t\t\t\t\t\tseriesLabel: seriesData.label,\n\t\t\t\t\t\tseriesIndex,\n\t\t\t\t\t\tdataPointIndex,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn flattened;\n\t}, [ series, mode ] );\n\n\t// Handle tooltip highlighting for keyboard navigation\n\tuseEffect( () => {\n\t\tif ( selectedIndex === undefined ) {\n\t\t\ttooltipContext?.hideTooltip();\n\t\t\treturn;\n\t\t}\n\n\t\tif ( mode === 'group' ) {\n\t\t\t// Show all series at the selected data point index in single tooltip.\n\t\t\tseries.forEach( ( s, index ) => {\n\t\t\t\tif ( selectedIndex < s.data.length ) {\n\t\t\t\t\tconst datum = s.data[ selectedIndex ];\n\n\t\t\t\t\ttooltipContext?.showTooltip( {\n\t\t\t\t\t\tdatum,\n\t\t\t\t\t\tkey: s.label,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} );\n\t\t} else if ( mode === 'individual' ) {\n\t\t\t// Show individual tooltips for each datapoint from each series.\n\t\t\tif ( selectedIndex < tooltipData.length ) {\n\t\t\t\tconst tooltipItem = tooltipData[ selectedIndex ];\n\n\t\t\t\ttooltipContext?.showTooltip( {\n\t\t\t\t\tdatum: tooltipItem.datum,\n\t\t\t\t\tkey: tooltipItem.seriesLabel,\n\t\t\t\t\tindex: tooltipItem.seriesIndex,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\t// Don't include tooltipContext in the dependency array to avoid loop.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [ selectedIndex, tooltipData, series ] );\n\n\t// Create a focusable renderTooltip that includes accessibility features\n\tconst focusableRenderTooltip = useMemo( () => {\n\t\tif ( ! renderTooltip ) return undefined;\n\n\t\treturn ( params: RenderTooltipParams< DataPointDate > ) => {\n\t\t\tconst tooltipContent = renderTooltip( params );\n\n\t\t\tif ( selectedIndex !== undefined ) {\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={ tooltipRef }\n\t\t\t\t\t\ttabIndex={ -1 }\n\t\t\t\t\t\trole=\"tooltip\"\n\t\t\t\t\t\taria-atomic=\"true\"\n\t\t\t\t\t\tclassName={ keyboardFocusedClassName }\n\t\t\t\t\t\tdata-testid={ `chart-tooltip-${ selectedIndex }` }\n\t\t\t\t\t\tkey={ `chart-tooltip-${ selectedIndex }` }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ tooltipContent }\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<div role=\"tooltip\" aria-live=\"polite\">\n\t\t\t\t\t{ tooltipContent }\n\t\t\t\t</div>\n\t\t\t);\n\t\t};\n\t}, [ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName ] );\n\n\treturn <Tooltip { ...props } renderTooltip={ focusableRenderTooltip } />;\n};\n\n// Keyboard navigation hook for charts\ninterface UseKeyboardNavigationProps {\n\tselectedIndex: number | undefined;\n\tsetSelectedIndex: ( index: number | undefined ) => void;\n\tisNavigating: boolean;\n\tsetIsNavigating: ( navigating: boolean ) => void;\n\tchartRef: React.RefObject< HTMLDivElement >;\n\t/**\n\t * Total number of navigation points (length of tooltip data array)\n\t */\n\ttotalPoints: number;\n}\n\nexport const useKeyboardNavigation = ( {\n\tselectedIndex,\n\tsetSelectedIndex,\n\tisNavigating,\n\tsetIsNavigating,\n\tchartRef,\n\ttotalPoints,\n}: UseKeyboardNavigationProps ) => {\n\t// Focus the tooltip as soon as it is rendered\n\tconst tooltipRef = useCallback(\n\t\t( element: HTMLDivElement | null ) => {\n\t\t\tif ( element && selectedIndex !== undefined ) {\n\t\t\t\telement.focus();\n\t\t\t}\n\t\t},\n\t\t[ selectedIndex ]\n\t);\n\n\t// On each focus of chart, reset the selectedIndex to 0, if keyboard navigation is not already active\n\tconst onChartFocus = useCallback( () => {\n\t\tif ( ! isNavigating && selectedIndex !== undefined ) {\n\t\t\tsetSelectedIndex( 0 );\n\t\t}\n\t}, [ isNavigating, selectedIndex, setSelectedIndex ] );\n\n\t// On each blur of chart, keyboard navigation should restart from first tooltip\n\tconst onChartBlur = useCallback( () => {\n\t\tsetIsNavigating( false );\n\t}, [ setIsNavigating ] );\n\n\tconst onChartKeyDown = useCallback(\n\t\t( event: React.KeyboardEvent< HTMLDivElement > ) => {\n\t\t\tif ( totalPoints === 0 ) return;\n\n\t\t\t// Keep focus on the chart if tab is pressed\n\t\t\tif ( event.key === 'Tab' ) {\n\t\t\t\tchartRef.current?.focus();\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst currentSelectedIndex = selectedIndex === undefined ? -1 : selectedIndex;\n\n\t\t\tif ( currentSelectedIndex + 1 >= totalPoints && [ 'ArrowRight' ].includes( event.key ) ) {\n\t\t\t\tchartRef.current?.focus();\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tevent.preventDefault();\n\n\t\t\tif ( [ 'ArrowRight' ].includes( event.key ) ) {\n\t\t\t\tsetIsNavigating( true );\n\t\t\t\tsetSelectedIndex( ( currentSelectedIndex + 1 ) % totalPoints );\n\t\t\t} else if ( [ 'ArrowLeft' ].includes( event.key ) ) {\n\t\t\t\tsetIsNavigating( true );\n\t\t\t\tsetSelectedIndex( ( currentSelectedIndex - 1 + totalPoints ) % totalPoints );\n\t\t\t} else if ( event.key === 'Escape' ) {\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\tchartRef.current?.focus();\n\t\t\t}\n\t\t},\n\t\t[ totalPoints, selectedIndex, setSelectedIndex, setIsNavigating, chartRef ]\n\t);\n\n\treturn {\n\t\ttooltipRef,\n\t\tonChartFocus,\n\t\tonChartBlur,\n\t\tonChartKeyDown,\n\t};\n};\n\n// Re-export the base Tooltip for backwards compatibility\nexport { Tooltip };\nexport type { BaseTooltipProps };\n"],"mappings":"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","names":["jsx"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/jetpack/jetpack/projects/js-packages/charts/dist/chunk-7HROSZRS.cjs","../src/components/tooltip/base-tooltip.tsx","../src/components/tooltip/base-tooltip.module.scss","../src/components/tooltip/accessible-tooltip.tsx"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/jetpack/jetpack/projects/js-packages/charts/dist/chunk-7HROSZRS.cjs","sourcesContent":[null,"import { formatNumber } from '@automattic/number-formatters';\nimport styles from './base-tooltip.module.scss';\nimport type { CSSProperties, ComponentType, ReactNode } from 'react';\n\ntype TooltipData = {\n\tlabel: string;\n\tvalue: number;\n\tvalueDisplay?: string;\n};\n\ntype TooltipComponentProps = {\n\tdata: TooltipData;\n\tclassName?: string;\n};\n\ntype TooltipCommonProps = {\n\ttop: number;\n\tleft: number;\n\tstyle?: CSSProperties;\n\tclassName?: string;\n\t/**\n\t * Whether to render the tooltip container div. When false, only renders the content.\n\t * Useful when the tooltip is rendered inside a portal or custom container.\n\t * @default true\n\t */\n\trenderContainer?: boolean;\n};\n\ntype DefaultDataTooltip = {\n\tdata: TooltipData;\n\tcomponent?: ComponentType< TooltipComponentProps >;\n\tchildren?: never;\n};\n\ntype CustomTooltip = {\n\tchildren: ReactNode;\n\tdata?: never;\n\tcomponent?: never;\n};\n\ntype BaseTooltipProps = TooltipCommonProps & ( DefaultDataTooltip | CustomTooltip );\n\nconst DefaultTooltipContent = ( { data }: TooltipComponentProps ) => (\n\t<>\n\t\t{ data?.label }: { data?.valueDisplay || formatNumber( data?.value ) }\n\t</>\n);\n\nexport const BaseTooltip = ( {\n\tdata,\n\ttop,\n\tleft,\n\tcomponent: Component = DefaultTooltipContent,\n\tchildren,\n\tclassName,\n\tstyle,\n\trenderContainer = true,\n}: BaseTooltipProps ) => {\n\tconst content = children || ( data && <Component data={ data } className={ className } /> );\n\n\tif ( ! renderContainer ) {\n\t\treturn content;\n\t}\n\n\treturn (\n\t\t<div className={ styles.tooltip } style={ { top, left, ...style } } role=\"tooltip\">\n\t\t\t{ content }\n\t\t</div>\n\t);\n};\n\nexport type { BaseTooltipProps, TooltipData };\n","import 'css-chunk:src/components/tooltip/base-tooltip.module.scss';export default {\n \"tooltip\": \"a8ccharts-OfX6nd\"\n};","import { Tooltip, TooltipContext } from '@visx/xychart';\nimport { useContext, useEffect, useCallback, useMemo } from 'react';\nimport type { SeriesData, DataPointDate } from '../../types';\nimport type {\n\tTooltipProps as BaseTooltipProps,\n\tRenderTooltipParams,\n} from '@visx/xychart/lib/components/Tooltip';\nimport type { ReactNode } from 'react';\n\n// Type for flattened tooltip data used in individual mode\nexport type FlattenedTooltipData = {\n\tdatum: DataPointDate;\n\tseriesLabel: string;\n\tseriesIndex: number;\n\tdataPointIndex: number;\n};\n\n// Enhanced tooltip with keyboard navigation and accessibility\ninterface AccessibleTooltipProps\n\textends Omit< BaseTooltipProps< DataPointDate >, 'renderTooltip' > {\n\trenderTooltip?: ( params: RenderTooltipParams< DataPointDate > ) => ReactNode;\n\tselectedIndex?: number | undefined;\n\ttooltipRef?: ( element: HTMLDivElement | null ) => void;\n\tkeyboardFocusedClassName?: string;\n\t/**\n\t * Flattened tooltip data prepared by parent component\n\t * Each index corresponds to one tooltip to show\n\t */\n\ttooltipData?: FlattenedTooltipData[];\n\t/**\n\t * For line charts: series data to show all series at selected data point\n\t * When provided, shows all series instead of individual tooltips\n\t */\n\tseries?: SeriesData[];\n\t/**\n\t * Whether to combine tooltip information from multiple series into a single tooltip. This is useful for line charts.\n\t * Or to show individual tooltips for each series. This is useful for bar charts.\n\t */\n\tmode?: 'individual' | 'group';\n}\n\nexport const AccessibleTooltip: React.FC< AccessibleTooltipProps > = ( {\n\trenderTooltip,\n\tselectedIndex,\n\ttooltipRef,\n\tkeyboardFocusedClassName,\n\tseries = [],\n\tmode = 'group',\n\t...props\n} ) => {\n\tconst tooltipContext = useContext( TooltipContext );\n\n\tconst tooltipData = useMemo( () => {\n\t\tif ( mode !== 'individual' ) return [];\n\t\tif ( series.length === 0 ) return [];\n\n\t\tconst maxDataPoints = Math.max( ...series.map( s => s.data.length ) );\n\t\tconst flattened: Array< {\n\t\t\tdatum: DataPointDate;\n\t\t\tseriesLabel: string;\n\t\t\tseriesIndex: number;\n\t\t\tdataPointIndex: number;\n\t\t} > = [];\n\n\t\t// Pattern: [series1[0], series2[0], series3[0], series1[1], series2[1], series3[1], ...]\n\t\tfor ( let dataPointIndex = 0; dataPointIndex < maxDataPoints; dataPointIndex++ ) {\n\t\t\tfor ( let seriesIndex = 0; seriesIndex < series.length; seriesIndex++ ) {\n\t\t\t\tconst seriesData = series[ seriesIndex ];\n\t\t\t\tif ( dataPointIndex < seriesData.data.length ) {\n\t\t\t\t\tflattened.push( {\n\t\t\t\t\t\tdatum: seriesData.data[ dataPointIndex ] as DataPointDate,\n\t\t\t\t\t\tseriesLabel: seriesData.label,\n\t\t\t\t\t\tseriesIndex,\n\t\t\t\t\t\tdataPointIndex,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn flattened;\n\t}, [ series, mode ] );\n\n\t// Handle tooltip highlighting for keyboard navigation\n\tuseEffect( () => {\n\t\tif ( selectedIndex === undefined ) {\n\t\t\ttooltipContext?.hideTooltip();\n\t\t\treturn;\n\t\t}\n\n\t\tif ( mode === 'group' ) {\n\t\t\t// Show all series at the selected data point index in single tooltip.\n\t\t\tseries.forEach( ( s, index ) => {\n\t\t\t\tif ( selectedIndex < s.data.length ) {\n\t\t\t\t\tconst datum = s.data[ selectedIndex ];\n\n\t\t\t\t\ttooltipContext?.showTooltip( {\n\t\t\t\t\t\tdatum,\n\t\t\t\t\t\tkey: s.label,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} );\n\t\t} else if ( mode === 'individual' ) {\n\t\t\t// Show individual tooltips for each datapoint from each series.\n\t\t\tif ( selectedIndex < tooltipData.length ) {\n\t\t\t\tconst tooltipItem = tooltipData[ selectedIndex ];\n\n\t\t\t\ttooltipContext?.showTooltip( {\n\t\t\t\t\tdatum: tooltipItem.datum,\n\t\t\t\t\tkey: tooltipItem.seriesLabel,\n\t\t\t\t\tindex: tooltipItem.seriesIndex,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\t// Don't include tooltipContext in the dependency array to avoid loop.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [ selectedIndex, tooltipData, series ] );\n\n\t// Create a focusable renderTooltip that includes accessibility features\n\tconst focusableRenderTooltip = useMemo( () => {\n\t\tif ( ! renderTooltip ) return undefined;\n\n\t\treturn ( params: RenderTooltipParams< DataPointDate > ) => {\n\t\t\tconst tooltipContent = renderTooltip( params );\n\n\t\t\tif ( selectedIndex !== undefined ) {\n\t\t\t\treturn (\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={ tooltipRef }\n\t\t\t\t\t\ttabIndex={ -1 }\n\t\t\t\t\t\trole=\"tooltip\"\n\t\t\t\t\t\taria-atomic=\"true\"\n\t\t\t\t\t\tclassName={ keyboardFocusedClassName }\n\t\t\t\t\t\tdata-testid={ `chart-tooltip-${ selectedIndex }` }\n\t\t\t\t\t\tkey={ `chart-tooltip-${ selectedIndex }` }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ tooltipContent }\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<div role=\"tooltip\" aria-live=\"polite\">\n\t\t\t\t\t{ tooltipContent }\n\t\t\t\t</div>\n\t\t\t);\n\t\t};\n\t}, [ renderTooltip, selectedIndex, tooltipRef, keyboardFocusedClassName ] );\n\n\treturn <Tooltip { ...props } renderTooltip={ focusableRenderTooltip } />;\n};\n\n// Keyboard navigation hook for charts\ninterface UseKeyboardNavigationProps {\n\tselectedIndex: number | undefined;\n\tsetSelectedIndex: ( index: number | undefined ) => void;\n\tisNavigating: boolean;\n\tsetIsNavigating: ( navigating: boolean ) => void;\n\tchartRef: React.RefObject< HTMLDivElement >;\n\t/**\n\t * Total number of navigation points (length of tooltip data array)\n\t */\n\ttotalPoints: number;\n}\n\nexport const useKeyboardNavigation = ( {\n\tselectedIndex,\n\tsetSelectedIndex,\n\tisNavigating,\n\tsetIsNavigating,\n\tchartRef,\n\ttotalPoints,\n}: UseKeyboardNavigationProps ) => {\n\t// Focus the tooltip as soon as it is rendered\n\tconst tooltipRef = useCallback(\n\t\t( element: HTMLDivElement | null ) => {\n\t\t\tif ( element && selectedIndex !== undefined ) {\n\t\t\t\telement.focus();\n\t\t\t}\n\t\t},\n\t\t[ selectedIndex ]\n\t);\n\n\t// On each focus of chart, reset the selectedIndex to 0, if keyboard navigation is not already active\n\tconst onChartFocus = useCallback( () => {\n\t\tif ( ! isNavigating && selectedIndex !== undefined ) {\n\t\t\tsetSelectedIndex( 0 );\n\t\t}\n\t}, [ isNavigating, selectedIndex, setSelectedIndex ] );\n\n\t// On each blur of chart, keyboard navigation should restart from first tooltip\n\tconst onChartBlur = useCallback( () => {\n\t\tsetIsNavigating( false );\n\t}, [ setIsNavigating ] );\n\n\tconst onChartKeyDown = useCallback(\n\t\t( event: React.KeyboardEvent< HTMLDivElement > ) => {\n\t\t\tif ( totalPoints === 0 ) return;\n\n\t\t\t// Keep focus on the chart if tab is pressed\n\t\t\tif ( event.key === 'Tab' ) {\n\t\t\t\tchartRef.current?.focus();\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst currentSelectedIndex = selectedIndex === undefined ? -1 : selectedIndex;\n\n\t\t\tif ( currentSelectedIndex + 1 >= totalPoints && [ 'ArrowRight' ].includes( event.key ) ) {\n\t\t\t\tchartRef.current?.focus();\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tevent.preventDefault();\n\n\t\t\tif ( [ 'ArrowRight' ].includes( event.key ) ) {\n\t\t\t\tsetIsNavigating( true );\n\t\t\t\tsetSelectedIndex( ( currentSelectedIndex + 1 ) % totalPoints );\n\t\t\t} else if ( [ 'ArrowLeft' ].includes( event.key ) ) {\n\t\t\t\tsetIsNavigating( true );\n\t\t\t\tsetSelectedIndex( ( currentSelectedIndex - 1 + totalPoints ) % totalPoints );\n\t\t\t} else if ( event.key === 'Escape' ) {\n\t\t\t\tsetSelectedIndex( undefined );\n\t\t\t\tsetIsNavigating( false );\n\t\t\t\tchartRef.current?.focus();\n\t\t\t}\n\t\t},\n\t\t[ totalPoints, selectedIndex, setSelectedIndex, setIsNavigating, chartRef ]\n\t);\n\n\treturn {\n\t\ttooltipRef,\n\t\tonChartFocus,\n\t\tonChartBlur,\n\t\tonChartKeyDown,\n\t};\n};\n\n// Re-export the base Tooltip for backwards compatibility\nexport { Tooltip };\nexport type { BaseTooltipProps };\n"]}