layerchart 2.0.0-next.52 → 2.0.0-next.54

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 (50) hide show
  1. package/dist/components/Arc.svelte +8 -7
  2. package/dist/components/Arc.svelte.test.js +1 -1
  3. package/dist/components/ArcLabel.svelte +1 -1
  4. package/dist/components/ClipPath.svelte +1 -1
  5. package/dist/components/GeoClipPath.svelte +75 -0
  6. package/dist/components/GeoClipPath.svelte.d.ts +29 -0
  7. package/dist/components/GeoLegend.svelte +44 -13
  8. package/dist/components/GeoLegend.svelte.d.ts +11 -0
  9. package/dist/components/Hull.svelte +20 -2
  10. package/dist/components/Hull.svelte.d.ts +2 -2
  11. package/dist/components/Pie.svelte +8 -2
  12. package/dist/components/Text.svelte +63 -16
  13. package/dist/components/Text.svelte.d.ts +10 -0
  14. package/dist/components/charts/BarChart.svelte.test.js +1 -1
  15. package/dist/components/charts/DefaultTooltip.svelte.test.js +18 -18
  16. package/dist/components/charts/LineChart.svelte.test.js +1 -1
  17. package/dist/components/charts/PieChart.svelte.test.js +2 -2
  18. package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-series-tooltip-should-use-explicit-series-colors--not-color-scale-1.png +0 -0
  19. package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-series-tooltip-should-use-explicit-series-colors--not-color-scale-2.png +0 -0
  20. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-fade-non-highlighted-tooltip-series-items-on-hover-1.png +0 -0
  21. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-fade-non-highlighted-tooltip-series-items-on-hover-2.png +0 -0
  22. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-header-and-all-series-items-1.png +0 -0
  23. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-header-and-all-series-items-2.png +0 -0
  24. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-series-colors-in-tooltip-items-1.png +0 -0
  25. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-series-colors-in-tooltip-items-2.png +0 -0
  26. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-single-series-without-total-1.png +0 -0
  27. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-AreaChart--multi-series--quadtree-x-mode--should-show-single-series-without-total-2.png +0 -0
  28. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-LineChart--multi-series--quadtree-x-mode--should-show-header-and-all-series-items-1.png +0 -0
  29. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-LineChart--multi-series--quadtree-x-mode--should-show-header-and-all-series-items-2.png +0 -0
  30. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-1.png +0 -0
  31. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-2.png +0 -0
  32. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-x--y--and-r-items-when-r-is-configured-1.png +0 -0
  33. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-x--y--and-r-items-when-r-is-configured-2.png +0 -0
  34. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-x-and-y-items-in-tooltip-1.png +0 -0
  35. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-x-and-y-items-in-tooltip-2.png +0 -0
  36. package/dist/components/charts/__screenshots__/LineChart.svelte.test.ts/LineChart-tooltip-should-prefer-cScale-color-over-default-series-color-when-cScale-is-explicitly-provided-1.png +0 -0
  37. package/dist/components/charts/__screenshots__/LineChart.svelte.test.ts/LineChart-tooltip-should-prefer-cScale-color-over-default-series-color-when-cScale-is-explicitly-provided-2.png +0 -0
  38. package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-1.png +0 -0
  39. package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-2.png +0 -0
  40. package/dist/components/index.d.ts +2 -0
  41. package/dist/components/index.js +2 -0
  42. package/dist/components/tooltip/Tooltip.svelte +145 -29
  43. package/dist/components/tooltip/Tooltip.svelte.d.ts +16 -0
  44. package/dist/components/tooltip/Tooltip.svelte.test.d.ts +1 -0
  45. package/dist/components/tooltip/Tooltip.svelte.test.js +294 -0
  46. package/dist/components/tooltip/__screenshots__/Tooltip.svelte.test.ts/Tooltip-portal-should-portal-tooltip-to-a-custom-selector-target-1.png +0 -0
  47. package/dist/components/tooltip/__screenshots__/Tooltip.svelte.test.ts/Tooltip-portal-should-portal-tooltip-to-a-custom-selector-target-2.png +0 -0
  48. package/dist/components/tooltip/__screenshots__/Tooltip.svelte.test.ts/Tooltip-portal-should-render-tooltip-inline-when-portal-is-false-1.png +0 -0
  49. package/dist/components/tooltip/__screenshots__/Tooltip.svelte.test.ts/Tooltip-portal-should-render-tooltip-inline-when-portal-is-false-2.png +0 -0
  50. package/package.json +13 -13
@@ -0,0 +1,294 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { render } from 'vitest-browser-svelte';
3
+ import LineChart from '../charts/LineChart.svelte';
4
+ const data = [
5
+ { date: 0, value: 10 },
6
+ { date: 1, value: 30 },
7
+ { date: 2, value: 20 },
8
+ { date: 3, value: 50 },
9
+ { date: 4, value: 40 },
10
+ ];
11
+ const baseProps = {
12
+ data,
13
+ x: 'date',
14
+ y: 'value',
15
+ height: 300,
16
+ width: 400,
17
+ tooltipContext: { mode: 'bisect-x' },
18
+ };
19
+ /** Dispatch pointer events to trigger the tooltip on a given element */
20
+ function triggerTooltip(el, position) {
21
+ const rect = el.getBoundingClientRect();
22
+ const eventInit = {
23
+ bubbles: true,
24
+ clientX: position?.clientX ?? rect.x + rect.width / 2,
25
+ clientY: position?.clientY ?? rect.y + rect.height / 2,
26
+ };
27
+ el.dispatchEvent(new PointerEvent('pointerenter', eventInit));
28
+ el.dispatchEvent(new PointerEvent('pointermove', eventInit));
29
+ }
30
+ describe('Tooltip', () => {
31
+ describe('portal', () => {
32
+ it('should portal tooltip to body by default', async () => {
33
+ const { container } = render(LineChart, {
34
+ props: baseProps,
35
+ });
36
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
37
+ await expect.element(tooltipCtx).toBeInTheDocument();
38
+ triggerTooltip(tooltipCtx);
39
+ await vi.waitFor(() => {
40
+ // Tooltip root should be portaled to body (outside the chart container)
41
+ const tooltipInBody = document.body.querySelector('.lc-tooltip-root');
42
+ expect(tooltipInBody).not.toBeNull();
43
+ // Should use fixed positioning when portaled
44
+ const style = getComputedStyle(tooltipInBody);
45
+ expect(style.position).toBe('fixed');
46
+ });
47
+ });
48
+ it('should render tooltip inline when portal is false', async () => {
49
+ const { container } = render(LineChart, {
50
+ props: {
51
+ ...baseProps,
52
+ props: { tooltip: { root: { portal: false } } },
53
+ },
54
+ });
55
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
56
+ await expect.element(tooltipCtx).toBeInTheDocument();
57
+ triggerTooltip(tooltipCtx);
58
+ await vi.waitFor(() => {
59
+ // Tooltip root should be inside the chart container
60
+ const tooltipInContainer = container.querySelector('.lc-tooltip-root');
61
+ expect(tooltipInContainer).not.toBeNull();
62
+ // Should use absolute positioning when not portaled
63
+ const style = getComputedStyle(tooltipInContainer);
64
+ expect(style.position).toBe('absolute');
65
+ });
66
+ });
67
+ it('should portal tooltip to a custom selector target', async () => {
68
+ // Create a custom portal target
69
+ const portalTarget = document.createElement('div');
70
+ portalTarget.className = 'custom-portal-target';
71
+ document.body.appendChild(portalTarget);
72
+ try {
73
+ const { container } = render(LineChart, {
74
+ props: {
75
+ ...baseProps,
76
+ props: { tooltip: { root: { portal: { target: '.custom-portal-target' } } } },
77
+ },
78
+ });
79
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
80
+ await expect.element(tooltipCtx).toBeInTheDocument();
81
+ triggerTooltip(tooltipCtx);
82
+ await vi.waitFor(() => {
83
+ const tooltipInTarget = portalTarget.querySelector('.lc-tooltip-root');
84
+ expect(tooltipInTarget).not.toBeNull();
85
+ });
86
+ }
87
+ finally {
88
+ portalTarget.remove();
89
+ }
90
+ });
91
+ it('should show tooltip content when portaled', async () => {
92
+ const { container } = render(LineChart, {
93
+ props: baseProps,
94
+ });
95
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
96
+ await expect.element(tooltipCtx).toBeInTheDocument();
97
+ triggerTooltip(tooltipCtx);
98
+ await vi.waitFor(() => {
99
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
100
+ expect(tooltipRoot).not.toBeNull();
101
+ // Should contain tooltip content (items from default tooltip)
102
+ const tooltipItems = tooltipRoot.querySelectorAll('.lc-tooltip-item-root');
103
+ expect(tooltipItems.length).toBeGreaterThan(0);
104
+ });
105
+ });
106
+ it('should have numeric top and left styles when portaled', async () => {
107
+ const { container } = render(LineChart, {
108
+ props: baseProps,
109
+ });
110
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
111
+ await expect.element(tooltipCtx).toBeInTheDocument();
112
+ triggerTooltip(tooltipCtx);
113
+ await vi.waitFor(() => {
114
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
115
+ expect(tooltipRoot).not.toBeNull();
116
+ // Should have valid pixel positions (not NaN or empty)
117
+ const top = tooltipRoot.style.top;
118
+ const left = tooltipRoot.style.left;
119
+ expect(top).toMatch(/^-?\d+(\.\d+)?px$/);
120
+ expect(left).toMatch(/^-?\d+(\.\d+)?px$/);
121
+ });
122
+ });
123
+ });
124
+ describe('contained="container" (default)', () => {
125
+ it('should flip tooltip left when pointer is near the right edge', async () => {
126
+ const { container } = render(LineChart, {
127
+ props: baseProps,
128
+ });
129
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
130
+ await expect.element(tooltipCtx).toBeInTheDocument();
131
+ const ctxRect = tooltipCtx.getBoundingClientRect();
132
+ // Trigger near the right edge of the container
133
+ triggerTooltip(tooltipCtx, {
134
+ clientX: ctxRect.right - 5,
135
+ clientY: ctxRect.top + ctxRect.height / 2,
136
+ });
137
+ await vi.waitFor(() => {
138
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
139
+ expect(tooltipRoot).not.toBeNull();
140
+ const tooltipLeft = parseFloat(tooltipRoot.style.left);
141
+ // Tooltip should be positioned to the LEFT of the pointer (flipped),
142
+ // so its left edge should be less than the pointer position
143
+ expect(tooltipLeft).toBeLessThan(ctxRect.right - 5);
144
+ // And specifically, the tooltip's right edge should not exceed the container
145
+ expect(tooltipLeft + tooltipRoot.offsetWidth).toBeLessThanOrEqual(ctxRect.right + 1);
146
+ });
147
+ });
148
+ it('should flip tooltip right when pointer is near the left edge', async () => {
149
+ const { container } = render(LineChart, {
150
+ props: {
151
+ ...baseProps,
152
+ props: { tooltip: { root: { anchor: 'top-right' } } },
153
+ },
154
+ });
155
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
156
+ await expect.element(tooltipCtx).toBeInTheDocument();
157
+ const ctxRect = tooltipCtx.getBoundingClientRect();
158
+ // Trigger near the left edge of the container
159
+ triggerTooltip(tooltipCtx, {
160
+ clientX: ctxRect.left + 5,
161
+ clientY: ctxRect.top + ctxRect.height / 2,
162
+ });
163
+ await vi.waitFor(() => {
164
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
165
+ expect(tooltipRoot).not.toBeNull();
166
+ const tooltipLeft = parseFloat(tooltipRoot.style.left);
167
+ // Tooltip should be positioned to the RIGHT of the pointer (flipped),
168
+ // so its left edge should be >= the container's left edge
169
+ expect(tooltipLeft).toBeGreaterThanOrEqual(ctxRect.left - 1);
170
+ });
171
+ });
172
+ it('should flip tooltip up when pointer is near the bottom edge', async () => {
173
+ const { container } = render(LineChart, {
174
+ props: baseProps,
175
+ });
176
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
177
+ await expect.element(tooltipCtx).toBeInTheDocument();
178
+ const ctxRect = tooltipCtx.getBoundingClientRect();
179
+ // Trigger near the bottom edge of the container
180
+ triggerTooltip(tooltipCtx, {
181
+ clientX: ctxRect.left + ctxRect.width / 2,
182
+ clientY: ctxRect.bottom - 5,
183
+ });
184
+ await vi.waitFor(() => {
185
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
186
+ expect(tooltipRoot).not.toBeNull();
187
+ const tooltipTop = parseFloat(tooltipRoot.style.top);
188
+ // Tooltip should be positioned ABOVE the pointer (flipped),
189
+ // so its bottom edge should not exceed the container
190
+ expect(tooltipTop + tooltipRoot.offsetHeight).toBeLessThanOrEqual(ctxRect.bottom + 1);
191
+ });
192
+ });
193
+ it('should flip tooltip down when pointer is near the top edge', async () => {
194
+ const { container } = render(LineChart, {
195
+ props: {
196
+ ...baseProps,
197
+ props: { tooltip: { root: { anchor: 'bottom-left' } } },
198
+ },
199
+ });
200
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
201
+ await expect.element(tooltipCtx).toBeInTheDocument();
202
+ const ctxRect = tooltipCtx.getBoundingClientRect();
203
+ // Trigger near the top edge of the container
204
+ triggerTooltip(tooltipCtx, {
205
+ clientX: ctxRect.left + ctxRect.width / 2,
206
+ clientY: ctxRect.top + 5,
207
+ });
208
+ await vi.waitFor(() => {
209
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
210
+ expect(tooltipRoot).not.toBeNull();
211
+ const tooltipTop = parseFloat(tooltipRoot.style.top);
212
+ // Tooltip should be positioned BELOW the pointer (flipped),
213
+ // so its top edge should be >= the container's top
214
+ expect(tooltipTop).toBeGreaterThanOrEqual(ctxRect.top - 1);
215
+ });
216
+ });
217
+ });
218
+ describe('contained="window"', () => {
219
+ it('should flip tooltip left when it would overflow the right side of the viewport', async () => {
220
+ const { container } = render(LineChart, {
221
+ props: {
222
+ ...baseProps,
223
+ props: { tooltip: { root: { contained: 'window' } } },
224
+ },
225
+ });
226
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
227
+ await expect.element(tooltipCtx).toBeInTheDocument();
228
+ const ctxRect = tooltipCtx.getBoundingClientRect();
229
+ // Trigger near the right edge of the container
230
+ triggerTooltip(tooltipCtx, {
231
+ clientX: ctxRect.right - 5,
232
+ clientY: ctxRect.top + ctxRect.height / 2,
233
+ });
234
+ await vi.waitFor(() => {
235
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
236
+ expect(tooltipRoot).not.toBeNull();
237
+ const tooltipLeft = parseFloat(tooltipRoot.style.left);
238
+ // Tooltip should not overflow the right side of the viewport
239
+ expect(tooltipLeft + tooltipRoot.offsetWidth).toBeLessThanOrEqual(window.innerWidth + 1);
240
+ });
241
+ });
242
+ it('should flip tooltip up when it would overflow the bottom of the viewport', async () => {
243
+ const { container } = render(LineChart, {
244
+ props: {
245
+ ...baseProps,
246
+ props: { tooltip: { root: { contained: 'window' } } },
247
+ },
248
+ });
249
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
250
+ await expect.element(tooltipCtx).toBeInTheDocument();
251
+ const ctxRect = tooltipCtx.getBoundingClientRect();
252
+ // Trigger near the bottom edge of the container
253
+ triggerTooltip(tooltipCtx, {
254
+ clientX: ctxRect.left + ctxRect.width / 2,
255
+ clientY: ctxRect.bottom - 5,
256
+ });
257
+ await vi.waitFor(() => {
258
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
259
+ expect(tooltipRoot).not.toBeNull();
260
+ const tooltipTop = parseFloat(tooltipRoot.style.top);
261
+ // Tooltip should not overflow the bottom of the viewport
262
+ expect(tooltipTop + tooltipRoot.offsetHeight).toBeLessThanOrEqual(window.innerHeight + 1);
263
+ });
264
+ });
265
+ });
266
+ describe('contained={false}', () => {
267
+ it('should not constrain tooltip position', async () => {
268
+ const { container } = render(LineChart, {
269
+ props: {
270
+ ...baseProps,
271
+ props: { tooltip: { root: { contained: false } } },
272
+ },
273
+ });
274
+ const tooltipCtx = container.querySelector('.lc-tooltip-context');
275
+ await expect.element(tooltipCtx).toBeInTheDocument();
276
+ const ctxRect = tooltipCtx.getBoundingClientRect();
277
+ // Trigger near the right edge — tooltip should NOT flip
278
+ triggerTooltip(tooltipCtx, {
279
+ clientX: ctxRect.right - 5,
280
+ clientY: ctxRect.top + ctxRect.height / 2,
281
+ });
282
+ await vi.waitFor(() => {
283
+ const tooltipRoot = document.body.querySelector('.lc-tooltip-root');
284
+ expect(tooltipRoot).not.toBeNull();
285
+ const tooltipLeft = parseFloat(tooltipRoot.style.left);
286
+ // With default anchor='top-left', tooltip is placed to the right of pointer.
287
+ // Since contained=false, the tooltip left should be near/past the pointer x
288
+ // (i.e., it doesn't flip like contained="container" would)
289
+ const pointerViewportX = ctxRect.right - 5;
290
+ expect(tooltipLeft).toBeGreaterThanOrEqual(pointerViewportX - 1);
291
+ });
292
+ });
293
+ });
294
+ });
package/package.json CHANGED
@@ -5,12 +5,12 @@
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
7
  "homepage": "https://layerchart.com",
8
- "version": "2.0.0-next.52",
8
+ "version": "2.0.0-next.54",
9
9
  "devDependencies": {
10
10
  "@changesets/cli": "^2.30.0",
11
11
  "@napi-rs/canvas": "^0.1.97",
12
12
  "@sveltejs/adapter-auto": "^7.0.1",
13
- "@sveltejs/kit": "^2.55.0",
13
+ "@sveltejs/kit": "^2.57.1",
14
14
  "@sveltejs/package": "^2.5.7",
15
15
  "@sveltejs/vite-plugin-svelte": "^7.0.0",
16
16
  "@svitejs/changesets-changelog-github-compact": "^1.2.0",
@@ -33,19 +33,19 @@
33
33
  "@types/d3-scale-chromatic": "^3.1.0",
34
34
  "@types/d3-shape": "^3.1.8",
35
35
  "@types/d3-time": "^3.0.4",
36
- "@vitest/browser": "^4.1.0",
37
- "@vitest/browser-playwright": "^4.1.0",
38
- "@vitest/ui": "^4.1.0",
39
- "playwright": "^1.58.2",
40
- "prettier": "^3.8.1",
36
+ "@vitest/browser": "^4.1.4",
37
+ "@vitest/browser-playwright": "^4.1.4",
38
+ "@vitest/ui": "^4.1.4",
39
+ "playwright": "^1.59.1",
40
+ "prettier": "^3.8.2",
41
41
  "prettier-plugin-svelte": "^3.5.1",
42
- "svelte": "5.54.1",
43
- "svelte-check": "^4.4.5",
44
- "svelte2tsx": "^0.7.52",
42
+ "svelte": "5.55.3",
43
+ "svelte-check": "^4.4.6",
44
+ "svelte2tsx": "^0.7.53",
45
45
  "tslib": "^2.8.1",
46
- "typescript": "^5.9.3",
47
- "vite": "^8.0.1",
48
- "vitest": "^4.1.0",
46
+ "typescript": "^6.0.2",
47
+ "vite": "^8.0.8",
48
+ "vitest": "^4.1.4",
49
49
  "vitest-browser-svelte": "^2.1.0"
50
50
  },
51
51
  "type": "module",