@automattic/charts 0.56.4 → 0.56.6
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.
- package/CHANGELOG.md +16 -0
- package/dist/charts/bar-chart/index.cjs +6 -6
- package/dist/charts/bar-chart/index.css +1 -4
- package/dist/charts/bar-chart/index.css.map +1 -1
- package/dist/charts/bar-chart/index.d.cts +2 -8
- package/dist/charts/bar-chart/index.d.ts +2 -8
- package/dist/charts/bar-chart/index.js +5 -5
- package/dist/charts/bar-list-chart/index.cjs +7 -7
- package/dist/charts/bar-list-chart/index.css +1 -4
- package/dist/charts/bar-list-chart/index.css.map +1 -1
- package/dist/charts/bar-list-chart/index.d.cts +2 -2
- package/dist/charts/bar-list-chart/index.d.ts +2 -2
- package/dist/charts/bar-list-chart/index.js +6 -6
- package/dist/charts/conversion-funnel-chart/index.cjs +5 -5
- package/dist/charts/conversion-funnel-chart/index.css +1 -4
- package/dist/charts/conversion-funnel-chart/index.css.map +1 -1
- package/dist/charts/conversion-funnel-chart/index.d.cts +2 -1
- package/dist/charts/conversion-funnel-chart/index.d.ts +2 -1
- package/dist/charts/conversion-funnel-chart/index.js +4 -4
- package/dist/charts/geo-chart/index.cjs +4 -4
- package/dist/charts/geo-chart/index.css +1 -4
- package/dist/charts/geo-chart/index.css.map +1 -1
- package/dist/charts/geo-chart/index.d.cts +2 -1
- package/dist/charts/geo-chart/index.d.ts +2 -1
- package/dist/charts/geo-chart/index.js +3 -3
- package/dist/charts/leaderboard-chart/index.cjs +5 -5
- package/dist/charts/leaderboard-chart/index.css +1 -4
- package/dist/charts/leaderboard-chart/index.css.map +1 -1
- package/dist/charts/leaderboard-chart/index.d.cts +3 -2
- package/dist/charts/leaderboard-chart/index.d.ts +3 -2
- package/dist/charts/leaderboard-chart/index.js +4 -4
- package/dist/charts/line-chart/index.cjs +6 -6
- package/dist/charts/line-chart/index.css +1 -4
- package/dist/charts/line-chart/index.css.map +1 -1
- package/dist/charts/line-chart/index.d.cts +2 -8
- package/dist/charts/line-chart/index.d.ts +2 -8
- package/dist/charts/line-chart/index.js +5 -5
- package/dist/charts/pie-chart/index.cjs +6 -4
- package/dist/charts/pie-chart/index.cjs.map +1 -1
- package/dist/charts/pie-chart/index.css +13 -7
- package/dist/charts/pie-chart/index.css.map +1 -1
- package/dist/charts/pie-chart/index.d.cts +2 -1
- package/dist/charts/pie-chart/index.d.ts +2 -1
- package/dist/charts/pie-chart/index.js +5 -3
- package/dist/charts/pie-semi-circle-chart/index.cjs +6 -4
- package/dist/charts/pie-semi-circle-chart/index.cjs.map +1 -1
- package/dist/charts/pie-semi-circle-chart/index.css +12 -13
- package/dist/charts/pie-semi-circle-chart/index.css.map +1 -1
- package/dist/charts/pie-semi-circle-chart/index.d.cts +5 -2
- package/dist/charts/pie-semi-circle-chart/index.d.ts +5 -2
- package/dist/charts/pie-semi-circle-chart/index.js +5 -3
- package/dist/charts/sparkline/index.cjs +7 -7
- package/dist/charts/sparkline/index.css +1 -4
- package/dist/charts/sparkline/index.css.map +1 -1
- package/dist/charts/sparkline/index.js +6 -6
- package/dist/{chunk-ZXEFMKVP.cjs → chunk-3EXJP67N.cjs} +7 -7
- package/dist/{chunk-ZXEFMKVP.cjs.map → chunk-3EXJP67N.cjs.map} +1 -1
- package/dist/{chunk-HNEG3EFJ.cjs → chunk-55ZCOYDF.cjs} +117 -132
- package/dist/chunk-55ZCOYDF.cjs.map +1 -0
- package/dist/{chunk-KKPZ4MVF.js → chunk-7FDQGBY7.js} +145 -119
- package/dist/chunk-7FDQGBY7.js.map +1 -0
- package/dist/{chunk-7QDEU3KN.cjs → chunk-ASLARV7L.cjs} +6 -6
- package/dist/chunk-ASLARV7L.cjs.map +1 -0
- package/dist/chunk-BXFD7JIG.cjs +401 -0
- package/dist/chunk-BXFD7JIG.cjs.map +1 -0
- package/dist/{chunk-KMYJJTSR.cjs → chunk-CAFJRZPZ.cjs} +12 -12
- package/dist/{chunk-KMYJJTSR.cjs.map → chunk-CAFJRZPZ.cjs.map} +1 -1
- package/dist/{chunk-WMWAUOQ4.js → chunk-E62LCBGD.js} +4 -4
- package/dist/{chunk-ZY4FXLMM.js → chunk-GWBS65VC.js} +3 -3
- package/dist/{chunk-MEIVKY4K.js → chunk-IS5YYLTV.js} +18 -18
- package/dist/{chunk-MEIVKY4K.js.map → chunk-IS5YYLTV.js.map} +1 -1
- package/dist/{chunk-5N77S5N3.cjs → chunk-K6TGILHX.cjs} +8 -8
- package/dist/{chunk-5N77S5N3.cjs.map → chunk-K6TGILHX.cjs.map} +1 -1
- package/dist/{chunk-EBDUXL5K.js → chunk-KHQPN77E.js} +3 -3
- package/dist/{chunk-SEKPIG5K.js → chunk-KNIMXN6Z.js} +2 -2
- package/dist/{chunk-SEKPIG5K.js.map → chunk-KNIMXN6Z.js.map} +1 -1
- package/dist/{chunk-M7PRGJFE.js → chunk-MDRCAGKZ.js} +4 -4
- package/dist/{chunk-RSYD434G.cjs → chunk-NQJE2CC7.cjs} +120 -98
- package/dist/chunk-NQJE2CC7.cjs.map +1 -0
- package/dist/{chunk-66BXSWMW.cjs → chunk-O2JIANHK.cjs} +25 -25
- package/dist/chunk-O2JIANHK.cjs.map +1 -0
- package/dist/{chunk-R23BFDIW.js → chunk-OMS5QIJN.js} +6 -6
- package/dist/chunk-OMS5QIJN.js.map +1 -0
- package/dist/{chunk-TYIH5LMV.js → chunk-OP6PHB2U.js} +6 -6
- package/dist/chunk-OP6PHB2U.js.map +1 -0
- package/dist/{chunk-AWNCAKZY.js → chunk-RFSHE3HL.js} +60 -16
- package/dist/chunk-RFSHE3HL.js.map +1 -0
- package/dist/{chunk-FZYJM5PN.js → chunk-SSFFCBCF.js} +6 -6
- package/dist/chunk-SSFFCBCF.js.map +1 -0
- package/dist/{chunk-I5467ZJ5.cjs → chunk-SUDERBUA.cjs} +2 -2
- package/dist/{chunk-I5467ZJ5.cjs.map → chunk-SUDERBUA.cjs.map} +1 -1
- package/dist/{chunk-SH32YSZO.cjs → chunk-UFRBUT2D.cjs} +19 -19
- package/dist/{chunk-SH32YSZO.cjs.map → chunk-UFRBUT2D.cjs.map} +1 -1
- package/dist/{chunk-CMHPXSCI.js → chunk-VPAEBI2F.js} +109 -87
- package/dist/chunk-VPAEBI2F.js.map +1 -0
- package/dist/{chunk-CERFRCXD.cjs → chunk-X7JL2NYJ.cjs} +24 -24
- package/dist/chunk-X7JL2NYJ.cjs.map +1 -0
- package/dist/{chunk-PGJAZN2H.js → chunk-XD2HV7M5.js} +77 -92
- package/dist/chunk-XD2HV7M5.js.map +1 -0
- package/dist/{chunk-GBDFC74U.cjs → chunk-YAXY5L7I.cjs} +7 -7
- package/dist/{chunk-GBDFC74U.cjs.map → chunk-YAXY5L7I.cjs.map} +1 -1
- package/dist/{chunk-LSV7F26B.cjs → chunk-YDVHT7GS.cjs} +77 -33
- package/dist/chunk-YDVHT7GS.cjs.map +1 -0
- package/dist/components/legend/index.cjs +2 -2
- package/dist/components/legend/index.css +1 -4
- package/dist/components/legend/index.css.map +1 -1
- package/dist/components/legend/index.d.cts +2 -1
- package/dist/components/legend/index.d.ts +2 -1
- package/dist/components/legend/index.js +1 -1
- package/dist/components/tooltip/index.d.cts +2 -1
- package/dist/components/tooltip/index.d.ts +2 -1
- package/dist/hooks/index.cjs +2 -2
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.css +1 -4
- package/dist/hooks/index.css.map +1 -1
- package/dist/hooks/index.d.cts +10 -7
- package/dist/hooks/index.d.ts +10 -7
- package/dist/hooks/index.js +3 -3
- package/dist/index.cjs +14 -14
- package/dist/index.css +24 -16
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -13
- package/dist/{leaderboard-chart-B5gWcqe7.d.ts → leaderboard-chart-BSgEw_Um.d.ts} +1 -1
- package/dist/{leaderboard-chart-C_6QDcqj.d.cts → leaderboard-chart-COtgamhe.d.cts} +1 -1
- package/dist/providers/index.cjs +2 -2
- package/dist/providers/index.css +1 -4
- package/dist/providers/index.css.map +1 -1
- package/dist/providers/index.d.cts +3 -2
- package/dist/providers/index.d.ts +3 -2
- package/dist/providers/index.js +1 -1
- package/dist/{themes-BDVaIfBz.d.cts → themes-CVR5rmIs.d.cts} +1 -1
- package/dist/{themes-mcS8QNkQ.d.ts → themes-DQzmaSze.d.ts} +1 -1
- package/dist/{types-BCFQlzTM.d.cts → types-CzdN7rUe.d.cts} +12 -3
- package/dist/{types-BCFQlzTM.d.ts → types-CzdN7rUe.d.ts} +12 -3
- package/dist/utils/index.d.cts +2 -1
- package/dist/utils/index.d.ts +2 -1
- package/package.json +10 -10
- package/src/charts/bar-chart/bar-chart.tsx +2 -9
- package/src/charts/bar-chart/test/bar-chart.test.tsx +3 -3
- package/src/charts/line-chart/line-chart.tsx +2 -2
- package/src/charts/line-chart/test/line-chart.test.tsx +3 -3
- package/src/charts/line-chart/types.ts +0 -7
- package/src/charts/pie-chart/pie-chart.module.scss +14 -3
- package/src/charts/pie-chart/pie-chart.tsx +172 -148
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +17 -11
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +147 -119
- package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +46 -6
- package/src/charts/private/with-responsive/test/with-responsive.test.tsx +5 -5
- package/src/charts/private/with-responsive/with-responsive.tsx +8 -7
- package/src/hooks/index.ts +1 -1
- package/src/hooks/test/use-chart-margin.test.tsx +44 -0
- package/src/hooks/test/{use-element-height.test.tsx → use-element-size.test.tsx} +45 -36
- package/src/hooks/use-chart-margin.tsx +92 -6
- package/src/hooks/use-element-size.ts +43 -0
- package/src/hooks/use-tooltip-portal-relocator.module.scss +1 -4
- package/src/hooks/use-tooltip-portal-relocator.ts +11 -0
- package/src/types.ts +13 -3
- package/dist/chunk-4YYROZDJ.cjs +0 -375
- package/dist/chunk-4YYROZDJ.cjs.map +0 -1
- package/dist/chunk-66BXSWMW.cjs.map +0 -1
- package/dist/chunk-7QDEU3KN.cjs.map +0 -1
- package/dist/chunk-AWNCAKZY.js.map +0 -1
- package/dist/chunk-CERFRCXD.cjs.map +0 -1
- package/dist/chunk-CMHPXSCI.js.map +0 -1
- package/dist/chunk-FZYJM5PN.js.map +0 -1
- package/dist/chunk-HNEG3EFJ.cjs.map +0 -1
- package/dist/chunk-KKPZ4MVF.js.map +0 -1
- package/dist/chunk-LSV7F26B.cjs.map +0 -1
- package/dist/chunk-PGJAZN2H.js.map +0 -1
- package/dist/chunk-R23BFDIW.js.map +0 -1
- package/dist/chunk-RSYD434G.cjs.map +0 -1
- package/dist/chunk-TYIH5LMV.js.map +0 -1
- package/src/hooks/use-element-height.ts +0 -37
- /package/dist/{chunk-WMWAUOQ4.js.map → chunk-E62LCBGD.js.map} +0 -0
- /package/dist/{chunk-ZY4FXLMM.js.map → chunk-GWBS65VC.js.map} +0 -0
- /package/dist/{chunk-EBDUXL5K.js.map → chunk-KHQPN77E.js.map} +0 -0
- /package/dist/{chunk-M7PRGJFE.js.map → chunk-MDRCAGKZ.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
2
|
-
import {
|
|
2
|
+
import { useElementSize } from '../use-element-size';
|
|
3
3
|
|
|
4
4
|
// Mock ResizeObserver
|
|
5
5
|
class MockResizeObserver {
|
|
@@ -25,13 +25,12 @@ class MockResizeObserver {
|
|
|
25
25
|
// Store original ResizeObserver
|
|
26
26
|
const originalResizeObserver = globalThis.ResizeObserver;
|
|
27
27
|
|
|
28
|
-
describe( '
|
|
28
|
+
describe( 'useElementSize', () => {
|
|
29
29
|
let mockResizeObserver;
|
|
30
30
|
|
|
31
31
|
beforeEach( () => {
|
|
32
32
|
mockResizeObserver = MockResizeObserver;
|
|
33
33
|
globalThis.ResizeObserver = mockResizeObserver;
|
|
34
|
-
globalThis.window.ResizeObserver = mockResizeObserver;
|
|
35
34
|
} );
|
|
36
35
|
|
|
37
36
|
afterEach( () => {
|
|
@@ -39,75 +38,82 @@ describe( 'useElementHeight', () => {
|
|
|
39
38
|
jest.clearAllMocks();
|
|
40
39
|
} );
|
|
41
40
|
|
|
42
|
-
it( 'should return initial
|
|
43
|
-
const { result } = renderHook( () =>
|
|
44
|
-
const [ refCallback, height ] = result.current;
|
|
41
|
+
it( 'should return initial dimensions of 0 by default', () => {
|
|
42
|
+
const { result } = renderHook( () => useElementSize() );
|
|
43
|
+
const [ refCallback, width, height ] = result.current;
|
|
45
44
|
|
|
45
|
+
expect( width ).toBe( 0 );
|
|
46
46
|
expect( height ).toBe( 0 );
|
|
47
47
|
expect( typeof refCallback ).toBe( 'function' );
|
|
48
48
|
} );
|
|
49
49
|
|
|
50
|
-
it( 'should return custom initial
|
|
51
|
-
const { result } = renderHook( () =>
|
|
52
|
-
|
|
50
|
+
it( 'should return custom initial dimensions when provided', () => {
|
|
51
|
+
const { result } = renderHook( () =>
|
|
52
|
+
useElementSize( { initialWidth: 200, initialHeight: 100 } )
|
|
53
|
+
);
|
|
54
|
+
const [ , width, height ] = result.current;
|
|
53
55
|
|
|
56
|
+
expect( width ).toBe( 200 );
|
|
54
57
|
expect( height ).toBe( 100 );
|
|
55
58
|
} );
|
|
56
59
|
|
|
57
|
-
it( 'should update
|
|
60
|
+
it( 'should update dimensions when element is attached', async () => {
|
|
58
61
|
const mockElement = {
|
|
59
|
-
getBoundingClientRect: jest.fn( () => ( { height: 150 } ) ),
|
|
62
|
+
getBoundingClientRect: jest.fn( () => ( { width: 300, height: 150 } ) ),
|
|
60
63
|
};
|
|
61
64
|
|
|
62
|
-
const { result } = renderHook( () =>
|
|
65
|
+
const { result } = renderHook( () => useElementSize() );
|
|
63
66
|
const [ refCallback ] = result.current;
|
|
64
67
|
|
|
65
68
|
// Attach the element
|
|
66
69
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
67
70
|
|
|
68
71
|
await waitFor( () => {
|
|
69
|
-
expect( result.current[ 1 ] ).toBe(
|
|
72
|
+
expect( result.current[ 1 ] ).toBe( 300 );
|
|
73
|
+
expect( result.current[ 2 ] ).toBe( 150 );
|
|
70
74
|
} );
|
|
71
75
|
|
|
72
76
|
expect( mockElement.getBoundingClientRect ).toHaveBeenCalled();
|
|
73
77
|
} );
|
|
74
78
|
|
|
75
|
-
it( 'should handle element with zero
|
|
79
|
+
it( 'should handle element with zero dimensions', async () => {
|
|
76
80
|
const mockElement = {
|
|
77
|
-
getBoundingClientRect: jest.fn( () => ( { height: 0 } ) ),
|
|
81
|
+
getBoundingClientRect: jest.fn( () => ( { width: 0, height: 0 } ) ),
|
|
78
82
|
};
|
|
79
83
|
|
|
80
|
-
const { result } = renderHook( () =>
|
|
84
|
+
const { result } = renderHook( () => useElementSize() );
|
|
81
85
|
const [ refCallback ] = result.current;
|
|
82
86
|
|
|
83
87
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
84
88
|
|
|
85
89
|
await waitFor( () => {
|
|
86
90
|
expect( result.current[ 1 ] ).toBe( 0 );
|
|
91
|
+
expect( result.current[ 2 ] ).toBe( 0 );
|
|
87
92
|
} );
|
|
88
93
|
} );
|
|
89
94
|
|
|
90
|
-
it( 'should handle getBoundingClientRect returning undefined
|
|
95
|
+
it( 'should handle getBoundingClientRect returning undefined dimensions', async () => {
|
|
91
96
|
const mockElement = {
|
|
92
|
-
getBoundingClientRect: jest.fn( () => ( { height: undefined } ) ),
|
|
97
|
+
getBoundingClientRect: jest.fn( () => ( { width: undefined, height: undefined } ) ),
|
|
93
98
|
};
|
|
94
99
|
|
|
95
|
-
const { result } = renderHook( () =>
|
|
100
|
+
const { result } = renderHook( () => useElementSize() );
|
|
96
101
|
const [ refCallback ] = result.current;
|
|
97
102
|
|
|
98
103
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
99
104
|
|
|
100
105
|
await waitFor( () => {
|
|
101
106
|
expect( result.current[ 1 ] ).toBe( 0 );
|
|
107
|
+
expect( result.current[ 2 ] ).toBe( 0 );
|
|
102
108
|
} );
|
|
103
109
|
} );
|
|
104
110
|
|
|
105
111
|
it( 'should disconnect previous observer when new element is attached', () => {
|
|
106
112
|
const mockElement1 = {
|
|
107
|
-
getBoundingClientRect: jest.fn( () => ( { height: 100 } ) ),
|
|
113
|
+
getBoundingClientRect: jest.fn( () => ( { width: 100, height: 100 } ) ),
|
|
108
114
|
};
|
|
109
115
|
const mockElement2 = {
|
|
110
|
-
getBoundingClientRect: jest.fn( () => ( { height: 200 } ) ),
|
|
116
|
+
getBoundingClientRect: jest.fn( () => ( { width: 200, height: 200 } ) ),
|
|
111
117
|
};
|
|
112
118
|
|
|
113
119
|
const disconnectSpy = jest.fn();
|
|
@@ -119,7 +125,7 @@ describe( 'useElementHeight', () => {
|
|
|
119
125
|
|
|
120
126
|
jest.spyOn( globalThis, 'ResizeObserver' ).mockImplementation( () => mockObserver );
|
|
121
127
|
|
|
122
|
-
const { result } = renderHook( () =>
|
|
128
|
+
const { result } = renderHook( () => useElementSize() );
|
|
123
129
|
const [ refCallback ] = result.current;
|
|
124
130
|
|
|
125
131
|
// Attach first element
|
|
@@ -133,7 +139,7 @@ describe( 'useElementHeight', () => {
|
|
|
133
139
|
|
|
134
140
|
it( 'should disconnect observer when element is removed (null)', () => {
|
|
135
141
|
const mockElement = {
|
|
136
|
-
getBoundingClientRect: jest.fn( () => ( { height: 100 } ) ),
|
|
142
|
+
getBoundingClientRect: jest.fn( () => ( { width: 100, height: 100 } ) ),
|
|
137
143
|
};
|
|
138
144
|
|
|
139
145
|
const disconnectSpy = jest.fn();
|
|
@@ -145,7 +151,7 @@ describe( 'useElementHeight', () => {
|
|
|
145
151
|
|
|
146
152
|
jest.spyOn( globalThis, 'ResizeObserver' ).mockImplementation( () => mockObserver );
|
|
147
153
|
|
|
148
|
-
const { result } = renderHook( () =>
|
|
154
|
+
const { result } = renderHook( () => useElementSize() );
|
|
149
155
|
const [ refCallback ] = result.current;
|
|
150
156
|
|
|
151
157
|
// Attach element
|
|
@@ -159,7 +165,7 @@ describe( 'useElementHeight', () => {
|
|
|
159
165
|
|
|
160
166
|
it( 'should create ResizeObserver and observe element', () => {
|
|
161
167
|
const mockElement = {
|
|
162
|
-
getBoundingClientRect: jest.fn( () => ( { height: 100 } ) ),
|
|
168
|
+
getBoundingClientRect: jest.fn( () => ( { width: 100, height: 100 } ) ),
|
|
163
169
|
};
|
|
164
170
|
|
|
165
171
|
const observeSpy = jest.fn();
|
|
@@ -171,7 +177,7 @@ describe( 'useElementHeight', () => {
|
|
|
171
177
|
|
|
172
178
|
jest.spyOn( globalThis, 'ResizeObserver' ).mockImplementation( () => mockObserver );
|
|
173
179
|
|
|
174
|
-
const { result } = renderHook( () =>
|
|
180
|
+
const { result } = renderHook( () => useElementSize() );
|
|
175
181
|
const [ refCallback ] = result.current;
|
|
176
182
|
|
|
177
183
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
@@ -181,7 +187,7 @@ describe( 'useElementHeight', () => {
|
|
|
181
187
|
} );
|
|
182
188
|
|
|
183
189
|
it( 'should maintain stable refCallback reference across re-renders', () => {
|
|
184
|
-
const { result, rerender } = renderHook( () =>
|
|
190
|
+
const { result, rerender } = renderHook( () => useElementSize() );
|
|
185
191
|
|
|
186
192
|
const firstRefCallback = result.current[ 0 ];
|
|
187
193
|
|
|
@@ -195,26 +201,27 @@ describe( 'useElementHeight', () => {
|
|
|
195
201
|
|
|
196
202
|
it( 'should work with different element types', async () => {
|
|
197
203
|
const mockSpanElement = {
|
|
198
|
-
getBoundingClientRect: jest.fn( () => ( { height: 50 } ) ),
|
|
204
|
+
getBoundingClientRect: jest.fn( () => ( { width: 75, height: 50 } ) ),
|
|
199
205
|
};
|
|
200
206
|
|
|
201
|
-
const { result } = renderHook( () =>
|
|
207
|
+
const { result } = renderHook( () => useElementSize() );
|
|
202
208
|
const [ refCallback ] = result.current;
|
|
203
209
|
|
|
204
210
|
refCallback( mockSpanElement as unknown as HTMLDivElement );
|
|
205
211
|
|
|
206
212
|
await waitFor( () => {
|
|
207
|
-
expect( result.current[ 1 ] ).toBe(
|
|
213
|
+
expect( result.current[ 1 ] ).toBe( 75 );
|
|
214
|
+
expect( result.current[ 2 ] ).toBe( 50 );
|
|
208
215
|
} );
|
|
209
216
|
} );
|
|
210
217
|
|
|
211
|
-
it( 'should update
|
|
218
|
+
it( 'should update dimensions when ResizeObserver callback is triggered', async () => {
|
|
212
219
|
let resizeCallback;
|
|
213
220
|
const mockElement = {
|
|
214
221
|
getBoundingClientRect: jest
|
|
215
222
|
.fn()
|
|
216
|
-
.mockReturnValueOnce( { height: 100 } )
|
|
217
|
-
.mockReturnValueOnce( {
|
|
223
|
+
.mockReturnValueOnce( { width: 100, height: 100 } )
|
|
224
|
+
.mockReturnValueOnce( { width: 200, height: 150 } ),
|
|
218
225
|
};
|
|
219
226
|
|
|
220
227
|
jest.spyOn( globalThis, 'ResizeObserver' ).mockImplementation( callback => {
|
|
@@ -226,13 +233,14 @@ describe( 'useElementHeight', () => {
|
|
|
226
233
|
};
|
|
227
234
|
} );
|
|
228
235
|
|
|
229
|
-
const { result } = renderHook( () =>
|
|
236
|
+
const { result } = renderHook( () => useElementSize() );
|
|
230
237
|
const [ refCallback ] = result.current;
|
|
231
238
|
|
|
232
239
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
233
240
|
|
|
234
241
|
await waitFor( () => {
|
|
235
242
|
expect( result.current[ 1 ] ).toBe( 100 );
|
|
243
|
+
expect( result.current[ 2 ] ).toBe( 100 );
|
|
236
244
|
} );
|
|
237
245
|
|
|
238
246
|
// Simulate resize
|
|
@@ -240,6 +248,7 @@ describe( 'useElementHeight', () => {
|
|
|
240
248
|
|
|
241
249
|
await waitFor( () => {
|
|
242
250
|
expect( result.current[ 1 ] ).toBe( 200 );
|
|
251
|
+
expect( result.current[ 2 ] ).toBe( 150 );
|
|
243
252
|
} );
|
|
244
253
|
|
|
245
254
|
expect( mockElement.getBoundingClientRect ).toHaveBeenCalledTimes( 2 );
|
|
@@ -247,7 +256,7 @@ describe( 'useElementHeight', () => {
|
|
|
247
256
|
|
|
248
257
|
it( 'should handle unmount properly', () => {
|
|
249
258
|
const mockElement = {
|
|
250
|
-
getBoundingClientRect: jest.fn( () => ( { height: 100 } ) ),
|
|
259
|
+
getBoundingClientRect: jest.fn( () => ( { width: 100, height: 100 } ) ),
|
|
251
260
|
};
|
|
252
261
|
|
|
253
262
|
const disconnectSpy = jest.fn();
|
|
@@ -257,7 +266,7 @@ describe( 'useElementHeight', () => {
|
|
|
257
266
|
unobserve: jest.fn(),
|
|
258
267
|
} ) );
|
|
259
268
|
|
|
260
|
-
const { result, unmount } = renderHook( () =>
|
|
269
|
+
const { result, unmount } = renderHook( () => useElementSize() );
|
|
261
270
|
const [ refCallback ] = result.current;
|
|
262
271
|
|
|
263
272
|
refCallback( mockElement as unknown as HTMLDivElement );
|
|
@@ -4,6 +4,79 @@ import { getLongestTickWidth } from '../utils';
|
|
|
4
4
|
import type { BaseChartProps, DataPointDate, SeriesData } from '../types';
|
|
5
5
|
import type { XYChartTheme } from '@visx/xychart';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Base top margin used when no dynamic adjustments are necessary.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_MARGIN_TOP = 10;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Base right margin used when no dynamic adjustments are necessary.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_MARGIN_RIGHT = 20;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base bottom margin used for charts with a bottom X-axis.
|
|
19
|
+
* This is large enough for typical font sizes and will be increased
|
|
20
|
+
* dynamically when tick labels require more space.
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_MARGIN_BOTTOM = 20;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Base left margin used when no dynamic adjustments are necessary.
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_MARGIN_LEFT = 20;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Bottom margin to use when the X-axis is rendered at the top.
|
|
31
|
+
* We only need a small buffer below the chart in that case.
|
|
32
|
+
*/
|
|
33
|
+
const DEFAULT_BOTTOM_FOR_TOP_AXIS = 10;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Fallback font size used when we cannot derive a font size
|
|
37
|
+
* from the theme or axis styles for X-axis tick labels.
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_FONT_SIZE = 12;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Fallback tick length used when tickLength is not provided
|
|
43
|
+
* by the theme for either axis.
|
|
44
|
+
*/
|
|
45
|
+
const DEFAULT_TICK_LENGTH = 8;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fallback width used for Y-axis tick labels when we cannot
|
|
49
|
+
* measure them via getLongestTickWidth.
|
|
50
|
+
*/
|
|
51
|
+
const DEFAULT_Y_TICK_WIDTH = 40;
|
|
52
|
+
|
|
53
|
+
const resolveFontSize = ( val?: number | string ): number | undefined => {
|
|
54
|
+
if ( typeof val === 'number' && ! isNaN( val ) ) {
|
|
55
|
+
return val;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if ( typeof val === 'string' ) {
|
|
59
|
+
const parsed = parseFloat( val );
|
|
60
|
+
return isNaN( parsed ) ? undefined : parsed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return undefined;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getXAxisLabelMetrics = ( theme: XYChartTheme, orientation: 'top' | 'bottom' ) => {
|
|
67
|
+
const xAxisStyles =
|
|
68
|
+
orientation === 'top' ? theme.axisStyles?.x?.top : theme.axisStyles?.x?.bottom;
|
|
69
|
+
|
|
70
|
+
const fontSize =
|
|
71
|
+
resolveFontSize( xAxisStyles?.axisLabel?.fontSize ) ||
|
|
72
|
+
resolveFontSize( theme.svgLabelSmall?.fontSize ) ||
|
|
73
|
+
DEFAULT_FONT_SIZE;
|
|
74
|
+
|
|
75
|
+
const tickLength = xAxisStyles?.tickLength ?? DEFAULT_TICK_LENGTH;
|
|
76
|
+
|
|
77
|
+
return { fontSize, tickLength };
|
|
78
|
+
};
|
|
79
|
+
|
|
7
80
|
export const useChartMargin = (
|
|
8
81
|
height: number,
|
|
9
82
|
options: BaseChartProps[ 'options' ],
|
|
@@ -34,8 +107,12 @@ export const useChartMargin = (
|
|
|
34
107
|
|
|
35
108
|
return useMemo( () => {
|
|
36
109
|
// Default margin is for bottom axis labels.
|
|
37
|
-
const defaultMargin = {
|
|
38
|
-
|
|
110
|
+
const defaultMargin = {
|
|
111
|
+
top: DEFAULT_MARGIN_TOP,
|
|
112
|
+
right: DEFAULT_MARGIN_RIGHT,
|
|
113
|
+
bottom: DEFAULT_MARGIN_BOTTOM,
|
|
114
|
+
left: DEFAULT_MARGIN_LEFT,
|
|
115
|
+
};
|
|
39
116
|
|
|
40
117
|
// Auto-calculate margin for y axis labels based on orientation and tick width.
|
|
41
118
|
const yAxisOrientation = options.axis?.y?.orientation;
|
|
@@ -46,7 +123,7 @@ export const useChartMargin = (
|
|
|
46
123
|
options.axis?.y?.tickFormat,
|
|
47
124
|
yAxisStyles.axisLabel
|
|
48
125
|
);
|
|
49
|
-
const yMarginValue = ( yTickWidth ??
|
|
126
|
+
const yMarginValue = ( yTickWidth ?? DEFAULT_Y_TICK_WIDTH ) + ( yAxisStyles?.tickLength ?? 0 );
|
|
50
127
|
|
|
51
128
|
if ( yAxisOrientation === 'right' ) {
|
|
52
129
|
defaultMargin.right = yMarginValue;
|
|
@@ -54,9 +131,18 @@ export const useChartMargin = (
|
|
|
54
131
|
defaultMargin.left = yMarginValue;
|
|
55
132
|
}
|
|
56
133
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
134
|
+
// Dynamically compute X-axis margin (bottom by default, or top if orientation is 'top').
|
|
135
|
+
// This mirrors Y-axis behavior where margin is based on label size and tick length,
|
|
136
|
+
// but keeps the padding minimal so consumers can control container spacing themselves.
|
|
137
|
+
const xOrientation = options.axis?.x?.orientation === 'top' ? 'top' : 'bottom';
|
|
138
|
+
const { fontSize, tickLength } = getXAxisLabelMetrics( theme, xOrientation );
|
|
139
|
+
const computedXMargin = fontSize + tickLength;
|
|
140
|
+
|
|
141
|
+
if ( xOrientation === 'top' ) {
|
|
142
|
+
defaultMargin.top = Math.max( defaultMargin.top, computedXMargin );
|
|
143
|
+
defaultMargin.bottom = DEFAULT_BOTTOM_FOR_TOP_AXIS;
|
|
144
|
+
} else {
|
|
145
|
+
defaultMargin.bottom = Math.max( defaultMargin.bottom, computedXMargin );
|
|
60
146
|
}
|
|
61
147
|
|
|
62
148
|
return defaultMargin;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook to measure the width and height of a DOM element.
|
|
5
|
+
* Returns a ref callback to attach to the element and the current dimensions in pixels.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} props - Optional props.
|
|
8
|
+
* @param {number} props.initialWidth - The initial width to use.
|
|
9
|
+
* @param {number} props.initialHeight - The initial height to use.
|
|
10
|
+
*
|
|
11
|
+
* @return {[Function, number, number]} A tuple containing a ref callback, width, and height in pixels
|
|
12
|
+
*/
|
|
13
|
+
export function useElementSize< T extends HTMLElement = HTMLDivElement >( {
|
|
14
|
+
initialWidth = 0,
|
|
15
|
+
initialHeight = 0,
|
|
16
|
+
}: {
|
|
17
|
+
initialWidth?: number;
|
|
18
|
+
initialHeight?: number;
|
|
19
|
+
} = {} ): [ ( node: T | null ) => void, number, number ] {
|
|
20
|
+
const [ width, setWidth ] = useState( initialWidth );
|
|
21
|
+
const [ height, setHeight ] = useState( initialHeight );
|
|
22
|
+
const observerRef = useRef< ResizeObserver | null >( null );
|
|
23
|
+
|
|
24
|
+
const refCallback = useCallback( ( node: T | null ) => {
|
|
25
|
+
if ( observerRef.current ) {
|
|
26
|
+
observerRef.current.disconnect();
|
|
27
|
+
observerRef.current = null;
|
|
28
|
+
}
|
|
29
|
+
if ( node ) {
|
|
30
|
+
const handleResize = () => {
|
|
31
|
+
const rect = node.getBoundingClientRect();
|
|
32
|
+
setWidth( rect.width || 0 );
|
|
33
|
+
setHeight( rect.height || 0 );
|
|
34
|
+
};
|
|
35
|
+
handleResize();
|
|
36
|
+
const resizeObserver = new ResizeObserver( handleResize );
|
|
37
|
+
resizeObserver.observe( node );
|
|
38
|
+
observerRef.current = resizeObserver;
|
|
39
|
+
}
|
|
40
|
+
}, [] );
|
|
41
|
+
|
|
42
|
+
return [ refCallback, width, height ];
|
|
43
|
+
}
|
|
@@ -107,6 +107,10 @@ export function useTooltipPortalRelocator(
|
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// Hide the portal immediately to prevent the tooltip from
|
|
111
|
+
// flashing at (0,0) before visx calculates the correct position.
|
|
112
|
+
node.style.opacity = '0';
|
|
113
|
+
|
|
110
114
|
// Position the portal at the viewport origin so visx's
|
|
111
115
|
// absolute-positioned tooltip coordinates remain correct.
|
|
112
116
|
// Zero-size with overflow: visible so it doesn't affect layout
|
|
@@ -131,6 +135,13 @@ export function useTooltipPortalRelocator(
|
|
|
131
135
|
if ( focusedElement ) {
|
|
132
136
|
focusedElement.focus();
|
|
133
137
|
}
|
|
138
|
+
|
|
139
|
+
// Reveal after two animation frames so visx has positioned the tooltip.
|
|
140
|
+
requestAnimationFrame( () => {
|
|
141
|
+
requestAnimationFrame( () => {
|
|
142
|
+
node.style.opacity = '';
|
|
143
|
+
} );
|
|
144
|
+
} );
|
|
134
145
|
};
|
|
135
146
|
|
|
136
147
|
// Patch document.body.removeChild so visx Portal unmount doesn't throw
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { LegendShape } from '@visx/legend/lib/types';
|
|
|
7
7
|
import type { ScaleInput, ScaleType } from '@visx/scale';
|
|
8
8
|
import type { TextProps } from '@visx/text/lib/Text';
|
|
9
9
|
import type { EventHandlerParams, GlyphProps, GridStyles, LineStyles } from '@visx/xychart';
|
|
10
|
+
import type { GapSize } from '@wordpress/theme';
|
|
10
11
|
import type { CSSProperties, PointerEvent, ReactNode } from 'react';
|
|
11
12
|
import type { GoogleDataTableColumn, GoogleDataTableRow } from 'react-google-charts';
|
|
12
13
|
|
|
@@ -364,15 +365,17 @@ export type BaseChartProps< T = DataPoint | DataPointDate | LeaderboardEntry > =
|
|
|
364
365
|
*/
|
|
365
366
|
className?: string;
|
|
366
367
|
/**
|
|
367
|
-
* Width of the chart in pixels
|
|
368
|
+
* Width of the chart container in pixels. When omitted, the chart fills its parent's width.
|
|
368
369
|
*/
|
|
369
370
|
width?: number;
|
|
370
371
|
/**
|
|
371
|
-
* Height of the chart in pixels
|
|
372
|
+
* Height of the chart container in pixels. When omitted, the chart fills its parent's height.
|
|
372
373
|
*/
|
|
373
374
|
height?: number;
|
|
374
375
|
/**
|
|
375
|
-
*
|
|
376
|
+
* Maximum diameter of the pie in pixels (pie and donut charts only).
|
|
377
|
+
* The pie will shrink if the container is smaller than this value.
|
|
378
|
+
* When omitted, the pie fills the available space.
|
|
376
379
|
*/
|
|
377
380
|
size?: number;
|
|
378
381
|
/**
|
|
@@ -457,6 +460,13 @@ export type BaseChartProps< T = DataPoint | DataPointDate | LeaderboardEntry > =
|
|
|
457
460
|
*/
|
|
458
461
|
animation?: boolean;
|
|
459
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Gap between chart elements (SVG, legend, children).
|
|
465
|
+
* Uses WordPress design system tokens.
|
|
466
|
+
* @default 'md'
|
|
467
|
+
*/
|
|
468
|
+
gap?: GapSize;
|
|
469
|
+
|
|
460
470
|
/**
|
|
461
471
|
* More options for the chart.
|
|
462
472
|
*/
|