@blotch/dolan 22.2.2 → 22.2.3

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 (154) hide show
  1. package/dist/commands/add.d.ts +6 -2
  2. package/dist/commands/add.js +32 -10
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/add.test.js +129 -9
  5. package/dist/commands/add.test.js.map +1 -1
  6. package/dist/commands/diff.d.ts +2 -1
  7. package/dist/commands/diff.js +38 -25
  8. package/dist/commands/diff.js.map +1 -1
  9. package/dist/commands/diff.test.js +72 -11
  10. package/dist/commands/diff.test.js.map +1 -1
  11. package/dist/commands/doctor.js +41 -27
  12. package/dist/commands/doctor.js.map +1 -1
  13. package/dist/commands/doctor.test.js +93 -9
  14. package/dist/commands/doctor.test.js.map +1 -1
  15. package/dist/commands/eject.js +7 -0
  16. package/dist/commands/eject.js.map +1 -1
  17. package/dist/commands/eject.test.js +39 -0
  18. package/dist/commands/eject.test.js.map +1 -1
  19. package/dist/commands/info.js +3 -2
  20. package/dist/commands/info.js.map +1 -1
  21. package/dist/commands/init.d.ts +37 -1
  22. package/dist/commands/init.js +149 -40
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/init.test.js +285 -26
  25. package/dist/commands/init.test.js.map +1 -1
  26. package/dist/commands/install-packages.d.ts +6 -0
  27. package/dist/commands/install-packages.js +20 -0
  28. package/dist/commands/install-packages.js.map +1 -0
  29. package/dist/commands/install-packages.test.d.ts +1 -0
  30. package/dist/commands/install-packages.test.js +55 -0
  31. package/dist/commands/install-packages.test.js.map +1 -0
  32. package/dist/commands/list.js +21 -14
  33. package/dist/commands/list.js.map +1 -1
  34. package/dist/commands/list.test.js +62 -5
  35. package/dist/commands/list.test.js.map +1 -1
  36. package/dist/commands/outdated.js +11 -10
  37. package/dist/commands/outdated.js.map +1 -1
  38. package/dist/commands/outdated.test.js +20 -5
  39. package/dist/commands/outdated.test.js.map +1 -1
  40. package/dist/commands/remove.d.ts +4 -0
  41. package/dist/commands/remove.js +17 -4
  42. package/dist/commands/remove.js.map +1 -1
  43. package/dist/commands/remove.test.js +53 -2
  44. package/dist/commands/remove.test.js.map +1 -1
  45. package/dist/commands/update.d.ts +6 -0
  46. package/dist/commands/update.js +59 -24
  47. package/dist/commands/update.js.map +1 -1
  48. package/dist/commands/update.test.js +273 -31
  49. package/dist/commands/update.test.js.map +1 -1
  50. package/dist/index.js +7 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/registry.d.ts +8 -0
  53. package/dist/registry.js +32 -24
  54. package/dist/registry.js.map +1 -1
  55. package/dist/registry.test.js +76 -37
  56. package/dist/registry.test.js.map +1 -1
  57. package/dist/testing/set-input-signal.d.ts +13 -0
  58. package/dist/testing/set-input-signal.js +20 -0
  59. package/dist/testing/set-input-signal.js.map +1 -0
  60. package/dist/testing/set-input-signal.test.d.ts +1 -0
  61. package/dist/testing/set-input-signal.test.js +20 -0
  62. package/dist/testing/set-input-signal.test.js.map +1 -0
  63. package/dist/ui/components/action-sheet/action-sheet.test.ts +48 -1
  64. package/dist/ui/components/action-sheet/action-sheet.ts +37 -9
  65. package/dist/ui/components/alert-dialog/alert-dialog.test.ts +49 -2
  66. package/dist/ui/components/alert-dialog/alert-dialog.ts +43 -12
  67. package/dist/ui/components/area-chart/area-chart-render.test.ts +52 -110
  68. package/dist/ui/components/area-chart/area-chart.test.ts +58 -123
  69. package/dist/ui/components/area-chart/area-chart.ts +100 -315
  70. package/dist/ui/components/area-chart/index.ts +2 -3
  71. package/dist/ui/components/bar-chart/bar-chart-render.test.ts +47 -163
  72. package/dist/ui/components/bar-chart/bar-chart.test.ts +83 -37
  73. package/dist/ui/components/bar-chart/bar-chart.ts +140 -294
  74. package/dist/ui/components/bar-chart/index.ts +3 -3
  75. package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +191 -9
  76. package/dist/ui/components/bottom-sheet/bottom-sheet.ts +136 -20
  77. package/dist/ui/components/button/button-render.test.ts +19 -0
  78. package/dist/ui/components/button/button.ts +18 -2
  79. package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +48 -163
  80. package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +106 -62
  81. package/dist/ui/components/candlestick-chart/candlestick-chart.ts +150 -323
  82. package/dist/ui/components/candlestick-chart/index.ts +3 -4
  83. package/dist/ui/components/chart/chart-focus.test.ts +132 -0
  84. package/dist/ui/components/chart/chart-focus.ts +115 -0
  85. package/dist/ui/components/chart/chart-presets.test.ts +139 -0
  86. package/dist/ui/components/chart/chart-presets.ts +271 -0
  87. package/dist/ui/components/chart/chart-render.test.ts +730 -0
  88. package/dist/ui/components/chart/chart-scene.test.ts +589 -0
  89. package/dist/ui/components/chart/chart-scene.ts +483 -0
  90. package/dist/ui/components/chart/chart-theme.test.ts +85 -0
  91. package/dist/ui/components/chart/chart-theme.ts +198 -0
  92. package/dist/ui/components/chart/chart-zoom.test.ts +159 -0
  93. package/dist/ui/components/chart/chart-zoom.ts +162 -0
  94. package/dist/ui/components/chart/chart.ts +921 -0
  95. package/dist/ui/components/chart/index.ts +24 -0
  96. package/dist/ui/components/dialog/dialog.test.ts +49 -2
  97. package/dist/ui/components/dialog/dialog.ts +43 -12
  98. package/dist/ui/components/icon/icon-theme.test.ts +133 -0
  99. package/dist/ui/components/icon/icon-theme.ts +204 -0
  100. package/dist/ui/components/icon/icon.test.ts +51 -2
  101. package/dist/ui/components/icon/icon.ts +38 -21
  102. package/dist/ui/components/line-chart/index.ts +2 -3
  103. package/dist/ui/components/line-chart/line-chart-render.test.ts +69 -107
  104. package/dist/ui/components/line-chart/line-chart.test.ts +63 -45
  105. package/dist/ui/components/line-chart/line-chart.ts +83 -241
  106. package/dist/ui/components/list/list.test.ts +69 -2
  107. package/dist/ui/components/list/list.ts +21 -15
  108. package/dist/ui/components/nav-drawer/nav-drawer.test.ts +80 -1
  109. package/dist/ui/components/nav-drawer/nav-drawer.ts +66 -11
  110. package/dist/ui/components/pagination/pagination.test.ts +62 -0
  111. package/dist/ui/components/pagination/pagination.ts +55 -8
  112. package/dist/ui/components/scatter-chart/index.ts +2 -3
  113. package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +51 -144
  114. package/dist/ui/components/scatter-chart/scatter-chart.test.ts +57 -25
  115. package/dist/ui/components/scatter-chart/scatter-chart.ts +104 -250
  116. package/dist/ui/components/scroll-area/scroll-area.test.ts +37 -2
  117. package/dist/ui/components/scroll-area/scroll-area.ts +8 -8
  118. package/dist/ui/components/spinner/spinner.test.ts +27 -0
  119. package/dist/ui/components/spinner/spinner.ts +33 -23
  120. package/dist/ui/components/toast/toast.test.ts +37 -0
  121. package/dist/ui/components/toast/toast.ts +20 -4
  122. package/dist/ui/theme/chart-colors.test.ts +30 -0
  123. package/dist/ui/theme/dark.css +8 -0
  124. package/dist/ui/theme/default.css +8 -0
  125. package/dist/ui/theme/icon-colors.test.ts +30 -0
  126. package/dist/ui/theme/tailwind-plugin.ts +10 -1
  127. package/dist/utils/installed.d.ts +17 -0
  128. package/dist/utils/installed.js +27 -0
  129. package/dist/utils/installed.js.map +1 -0
  130. package/dist/utils/installed.test.d.ts +1 -0
  131. package/dist/utils/installed.test.js +41 -0
  132. package/dist/utils/installed.test.js.map +1 -0
  133. package/dist/utils/interactive.d.ts +9 -0
  134. package/dist/utils/interactive.js +10 -0
  135. package/dist/utils/interactive.js.map +1 -0
  136. package/dist/utils/interactive.test.d.ts +1 -0
  137. package/dist/utils/interactive.test.js +24 -0
  138. package/dist/utils/interactive.test.js.map +1 -0
  139. package/dist/utils/packages.d.ts +36 -0
  140. package/dist/utils/packages.js +70 -0
  141. package/dist/utils/packages.js.map +1 -0
  142. package/dist/utils/packages.test.d.ts +1 -0
  143. package/dist/utils/packages.test.js +89 -0
  144. package/dist/utils/packages.test.js.map +1 -0
  145. package/dist/utils/resolve-paths.d.ts +17 -1
  146. package/dist/utils/resolve-paths.js +23 -3
  147. package/dist/utils/resolve-paths.js.map +1 -1
  148. package/dist/utils/resolve-paths.test.js +34 -2
  149. package/dist/utils/resolve-paths.test.js.map +1 -1
  150. package/package.json +5 -1
  151. package/dist/ui/components/cartesian-chart/cartesian-chart-render.test.ts +0 -188
  152. package/dist/ui/components/cartesian-chart/cartesian-chart.test.ts +0 -525
  153. package/dist/ui/components/cartesian-chart/cartesian-chart.ts +0 -1140
  154. package/dist/ui/components/cartesian-chart/index.ts +0 -18
@@ -1,174 +1,59 @@
1
- import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
1
+ import { render } from '@blotch/angular-lynx-testing-library';
2
+ import { createChartRuntime } from '@tanstack/charts/runtime';
3
+ import type {
4
+ ChartDefinition,
5
+ ChartPoint,
6
+ SceneNode,
7
+ } from '@tanstack/charts/types';
2
8
  import { describe, expect, it, vi } from 'vitest';
3
9
  import { setInputSignal } from '../../../test-utils/set-input-signal';
4
- import { UiCartesianChart } from '../cartesian-chart/cartesian-chart';
5
- import {
6
- UiCandlestickChart,
7
- UiCandlestickSeries,
8
- type CandlestickPoint,
9
- } from './candlestick-chart';
10
-
11
- const period = (
12
- x: number,
13
- o: number,
14
- h: number,
15
- l: number,
16
- c: number,
17
- ): CandlestickPoint => ({ x, open: o, high: h, low: l, close: c });
10
+ import { UiCandlestickChart } from './candlestick-chart';
11
+
12
+ const data = [
13
+ { x: 0, open: 100, high: 104, low: 98, close: 102 },
14
+ { x: 1, open: 102, high: 103, low: 97, close: 99 },
15
+ ];
16
+
17
+ const render2 = (definition: ChartDefinition<any, any, any>) => {
18
+ const scene = createChartRuntime().render(definition, {
19
+ width: 300,
20
+ height: 200,
21
+ });
22
+ const found: SceneNode[] = [];
23
+ const walk = (nodes: readonly SceneNode[]) => {
24
+ for (const node of nodes) {
25
+ if (node.kind === 'group') {
26
+ if (!node.focus) walk(node.children);
27
+ } else if (node.kind !== 'label') found.push(node);
28
+ }
29
+ };
30
+ walk(scene.nodes);
31
+ return { scene, found };
32
+ };
18
33
 
19
34
  describe('UiCandlestickChart', () => {
20
- it('renders with default configuration', async () => {
21
- const { container } = await render(UiCandlestickChart);
22
- expect(container).toBeTruthy();
23
- });
24
-
25
- it('derives the y-axis from the lowest low and highest high', async () => {
26
- const { componentRef } = await render(UiCandlestickChart);
27
- const inst = componentRef.instance as UiCandlestickChart;
28
- setInputSignal(inst.data, [
29
- period(0, 10, 15, 5, 12),
30
- period(1, 12, 20, 8, 18),
31
- period(2, 18, 22, 14, 16),
32
- ]);
33
- await waitForUpdate();
34
- const cast = inst as unknown as {
35
- xAxis: () => { domain: readonly [number, number] };
36
- yAxis: () => { domain: readonly [number, number] };
37
- effectivePadding: () => { left?: number; right?: number };
38
- };
39
- expect(cast.xAxis().domain[0]).toBeLessThanOrEqual(0);
40
- expect(cast.xAxis().domain[1]).toBeGreaterThanOrEqual(2);
41
- expect(cast.yAxis().domain[0]).toBeLessThanOrEqual(5);
42
- expect(cast.yAxis().domain[1]).toBeGreaterThanOrEqual(22);
43
- // 3 candles → half = 0.5 / 2 = 0.25
44
- expect(cast.effectivePadding().left).toBeCloseTo(0.25);
45
- expect(cast.effectivePadding().right).toBeCloseTo(0.25);
46
- });
47
-
48
- it('honors explicit xMin / xMax / yMin / yMax overrides', async () => {
49
- const { componentRef } = await render(UiCandlestickChart);
50
- const inst = componentRef.instance as UiCandlestickChart;
51
- setInputSignal(inst.data, [period(0, 10, 15, 5, 12)]);
52
- setInputSignal(inst.xMin, -5);
53
- setInputSignal(inst.xMax, 20);
54
- setInputSignal(inst.yMin, 0);
55
- setInputSignal(inst.yMax, 100);
56
- await waitForUpdate();
57
- const cast = inst as unknown as {
58
- xAxis: () => { domain: readonly [number, number] };
59
- yAxis: () => { domain: readonly [number, number] };
60
- };
61
- expect(cast.xAxis().domain[0]).toBeLessThanOrEqual(-5);
62
- expect(cast.xAxis().domain[1]).toBeGreaterThanOrEqual(20);
63
- expect(cast.yAxis().domain[0]).toBeLessThanOrEqual(0);
64
- expect(cast.yAxis().domain[1]).toBeGreaterThanOrEqual(100);
65
- });
66
-
67
- it('single-datum series drops the half-candle padding', async () => {
68
- const { componentRef } = await render(UiCandlestickChart);
69
- const inst = componentRef.instance as UiCandlestickChart;
70
- setInputSignal(inst.data, [period(0, 10, 15, 5, 12)]);
71
- await waitForUpdate();
72
- const cast = inst as unknown as {
73
- effectivePadding: () => { left?: number; right?: number };
74
- };
75
- expect(cast.effectivePadding().left).toBe(0);
76
- expect(cast.effectivePadding().right).toBe(0);
77
- });
35
+ type Internals = {
36
+ definition: () => ChartDefinition<any, any, any>;
37
+ onSelect(point: ChartPoint | null): void;
38
+ };
78
39
 
79
- it('user padding overrides the auto half-candle inset per side', async () => {
40
+ it('builds its definition from its inputs', async () => {
80
41
  const { componentRef } = await render(UiCandlestickChart);
81
- const inst = componentRef.instance as UiCandlestickChart;
82
- setInputSignal(inst.data, [
83
- period(0, 10, 15, 5, 12),
84
- period(1, 12, 20, 8, 18),
85
- ]);
86
- setInputSignal(inst.padding, { right: 0.05 });
87
- await waitForUpdate();
88
- const cast = inst as unknown as {
89
- effectivePadding: () => { left?: number; right?: number };
90
- };
91
- expect(cast.effectivePadding().right).toBeCloseTo(0.05);
92
- expect(cast.effectivePadding().left).toBeCloseTo(0.5);
42
+ const chart = componentRef.instance as UiCandlestickChart;
43
+ setInputSignal(chart.data, data);
44
+ setInputSignal(chart.upColor, 'blue');
45
+ const { found } = render2((chart as unknown as Internals).definition());
46
+ expect(found.filter((n) => n.kind === 'rect')[0]!.style?.fill).toBe('blue');
93
47
  });
94
48
 
95
- it('exposes the default tick formatter used by the child cartesian chart', async () => {
49
+ it('emits the tapped datum', async () => {
96
50
  const { componentRef } = await render(UiCandlestickChart);
97
- const inst = componentRef.instance as UiCandlestickChart;
98
- expect(inst.xTickFormat()(3.14159)).toBe('3.14');
99
- expect(inst.yTickFormat()(1.005)).toBe('1');
100
- });
101
- });
102
-
103
- describe('UiCandlestickSeries', () => {
104
- const renderSeries = async () => {
105
- const parentResult = await render(UiCartesianChart);
106
- const parent = parentResult.componentRef.instance as UiCartesianChart;
107
- setInputSignal(parent.xDomain, [0, 10]);
108
- setInputSignal(parent.yDomain, [0, 100]);
109
- await waitForUpdate();
110
- const seriesResult = await render(UiCandlestickSeries, {
111
- providers: [{ provide: UiCartesianChart, useValue: parent }],
112
- });
113
- const series = seriesResult.componentRef.instance as UiCandlestickSeries;
114
- setInputSignal(series.data, [
115
- // up candle (close >= open)
116
- period(0, 10, 20, 5, 15),
117
- // down candle (close < open)
118
- period(5, 60, 70, 50, 55),
119
- // doji (close === open)
120
- period(10, 40, 50, 30, 40),
121
- ]);
122
- await waitForUpdate();
123
- return { parent, series };
124
- };
125
-
126
- it('renders a wick + body for each period, tinted by direction', async () => {
127
- const { series } = await renderSeries();
128
- const cast = series as unknown as {
129
- candles: () => {
130
- wickStyle: string;
131
- bodyStyle: string;
132
- point: CandlestickPoint;
133
- }[];
134
- };
135
- const candles = cast.candles();
136
- expect(candles.length).toBe(3);
137
- // First: up candle → uses upColor (green rgba)
138
- expect(candles[0].bodyStyle).toContain('rgba(34, 197, 94, 1)');
139
- // Second: down candle → uses downColor (red rgba)
140
- expect(candles[1].bodyStyle).toContain('rgba(239, 68, 68, 1)');
141
- // Third: doji (open === close) is `up: true` (>= comparison)
142
- expect(candles[2].bodyStyle).toContain('rgba(34, 197, 94, 1)');
143
- });
144
-
145
- it('emits candleTap with the source point when a body is tapped', async () => {
146
- const { series } = await renderSeries();
147
- const cb = vi.fn();
148
- series.candleTap.subscribe(cb);
149
- const p = period(1, 2, 3, 1, 2);
150
- (
151
- series as unknown as { onCandleTap: (point: CandlestickPoint) => void }
152
- ).onCandleTap(p);
153
- expect(cb).toHaveBeenCalledWith(p);
154
- });
155
-
156
- it('single-datum series uses the fallback body width', async () => {
157
- const parentResult = await render(UiCartesianChart);
158
- const parent = parentResult.componentRef.instance as UiCartesianChart;
159
- setInputSignal(parent.xDomain, [0, 10]);
160
- setInputSignal(parent.yDomain, [0, 100]);
161
- await waitForUpdate();
162
- const seriesResult = await render(UiCandlestickSeries, {
163
- providers: [{ provide: UiCartesianChart, useValue: parent }],
164
- });
165
- const series = seriesResult.componentRef.instance as UiCandlestickSeries;
166
- setInputSignal(series.data, [period(5, 10, 15, 5, 12)]);
167
- await waitForUpdate();
168
- const cast = series as unknown as {
169
- candles: () => { bodyStyle: string }[];
170
- };
171
- // fallback body width = 8px
172
- expect(cast.candles()[0].bodyStyle).toContain('width: 8.00px');
51
+ const chart = componentRef.instance as UiCandlestickChart;
52
+ const tapped = vi.fn();
53
+ chart.candleTap.subscribe(tapped);
54
+ const internals = chart as unknown as Internals;
55
+ internals.onSelect({ datum: data[0] } as ChartPoint);
56
+ internals.onSelect(null);
57
+ expect(tapped.mock.calls).toEqual([[data[0]]]);
173
58
  });
174
59
  });
@@ -1,74 +1,118 @@
1
+ import { createChartRuntime } from '@tanstack/charts/runtime';
2
+ import { createChartTooltipContent } from '@tanstack/charts/tooltip/model';
3
+ import type {
4
+ ChartDefinition,
5
+ ChartTooltipOptions,
6
+ SceneNode,
7
+ } from '@tanstack/charts/types';
1
8
  import { describe, expect, it } from 'vitest';
2
- import { computeCandles } from './candlestick-chart';
9
+ import { candlestickChartDefinition } from './candlestick-chart';
3
10
 
4
- // A rising candle centered at cx=50. Screen y grows downward, so the projected
5
- // highY (top of the wick) is the smallest pixel value and lowY the largest.
6
- // close is above open (up), so closeY < openY in pixels.
7
- const upCandle = {
8
- cx: 50,
9
- highY: 10, // high price → top of plot
10
- lowY: 90, // low price → bottom of plot
11
- openY: 70,
12
- closeY: 30,
13
- up: true,
14
- };
11
+ const data = [
12
+ { x: 0, open: 100, high: 104, low: 98, close: 102 },
13
+ { x: 1, open: 102, high: 103, low: 97, close: 99 },
14
+ ];
15
15
 
16
- describe('computeCandles', () => {
17
- it('draws the wick spanning high to low, centered on cx', () => {
18
- const [shape] = computeCandles([upCandle], 12);
19
- expect(shape.wick.top).toBe(10); // min(highY, lowY)
20
- expect(shape.wick.height).toBe(80); // |lowY - highY|
21
- expect(shape.wick.width).toBe(1); // default wickWidth
22
- // Centered: left = cx - wickWidth/2.
23
- expect(shape.wick.left).toBe(50 - 0.5);
16
+ const render2 = (definition: ChartDefinition<any, any, any>) => {
17
+ const scene = createChartRuntime().render(definition, {
18
+ width: 300,
19
+ height: 200,
24
20
  });
21
+ const found: SceneNode[] = [];
22
+ const walk = (nodes: readonly SceneNode[]) => {
23
+ for (const node of nodes) {
24
+ if (node.kind === 'group') {
25
+ if (!node.focus) walk(node.children);
26
+ } else if (node.kind !== 'label') found.push(node);
27
+ }
28
+ };
29
+ walk(scene.nodes);
30
+ return { scene, found };
31
+ };
25
32
 
26
- it('draws the body between open and close, centered on cx', () => {
27
- const [shape] = computeCandles([upCandle], 12);
28
- expect(shape.body.top).toBe(30); // min(openY, closeY) — the higher price
29
- expect(shape.body.height).toBe(40); // |closeY - openY|
30
- expect(shape.body.width).toBe(12);
31
- expect(shape.body.left).toBe(50 - 6); // cx - bodyWidth/2
32
- expect(shape.up).toBe(true);
33
+ describe('candlestickChartDefinition', () => {
34
+ it('draws a wick and a body per candle, colored by direction', () => {
35
+ const { scene, found } = render2(candlestickChartDefinition({ data }));
36
+ const bodies = found.filter((n) => n.kind === 'rect');
37
+ expect(bodies.map((body) => body.style?.fill)).toEqual([
38
+ 'rgba(34, 197, 94, 1)',
39
+ 'rgba(239, 68, 68, 1)',
40
+ ]);
41
+ // The y axis spans the lows and highs.
42
+ expect(scene.scales['y']!.domain[0]).toBeLessThanOrEqual(97);
43
+ expect(scene.scales['y']!.domain[1]).toBeGreaterThanOrEqual(104);
44
+ expect(found.some((n) => n.kind === 'rule' || n.kind === 'polyline')).toBe(
45
+ true,
46
+ );
33
47
  });
34
48
 
35
- it('marks a falling candle as down and still tops the body at the higher price', () => {
36
- // close below open: closeY (pixels) is BELOW openY, i.e. larger.
37
- const downCandle = {
38
- cx: 20,
39
- highY: 5,
40
- lowY: 95,
41
- openY: 30,
42
- closeY: 60,
43
- up: false,
49
+ it('lists all four prices in the tooltip', () => {
50
+ const definition = candlestickChartDefinition({
51
+ data,
52
+ upColor: 'green',
53
+ downColor: 'red',
54
+ });
55
+ const { scene } = render2(definition);
56
+ const point = scene.points.find((p) => (p.datum as { x: number }).x === 1)!;
57
+ const content = createChartTooltipContent(
58
+ [point],
59
+ scene,
60
+ false,
61
+ definition.tooltip as unknown as ChartTooltipOptions,
62
+ point,
63
+ );
64
+ expect(content).toEqual({
65
+ title: '1',
66
+ color: 'red',
67
+ rows: [
68
+ { label: 'Open', value: '102' },
69
+ { label: 'High', value: '103' },
70
+ { label: 'Low', value: '97' },
71
+ { label: 'Close', value: '99' },
72
+ ],
73
+ });
74
+ const custom = (
75
+ definition.tooltip as unknown as Required<ChartTooltipOptions>
76
+ ).content;
77
+ const context = {
78
+ pinned: false,
79
+ xLabel: 'x',
80
+ yLabel: 'y',
81
+ formatX: (v: unknown) => `x${v}`,
82
+ formatY: (v: unknown) => `y${v}`,
44
83
  };
45
- const [shape] = computeCandles([downCandle], 10);
46
- expect(shape.up).toBe(false);
47
- expect(shape.body.top).toBe(30); // min(openY, closeY) = openY here
48
- expect(shape.body.height).toBe(30);
49
- });
50
-
51
- it('clamps a doji (open === close) to the minimum body height', () => {
52
- const doji = {
53
- cx: 0,
54
- highY: 0,
55
- lowY: 100,
56
- openY: 50,
57
- closeY: 50,
58
- up: true,
59
- };
60
- // Zero-height body would be invisible; the floor keeps it a thin line.
61
- expect(computeCandles([doji], 12)[0].body.height).toBe(1);
62
- expect(computeCandles([doji], 12, 1, 3)[0].body.height).toBe(3);
63
- });
64
-
65
- it('honors a custom wick width', () => {
66
- const [shape] = computeCandles([upCandle], 12, 3);
67
- expect(shape.wick.width).toBe(3);
68
- expect(shape.wick.left).toBe(50 - 1.5);
84
+ expect(custom([], context)).toEqual({ title: undefined, rows: [] });
85
+ // Points of other marks (say, a moving average added on top) get x/y rows.
86
+ expect(
87
+ custom(
88
+ [{ datum: { x: 1, y: 2 }, xValue: 1, yValue: 2 } as never],
89
+ context,
90
+ ),
91
+ ).toEqual({ title: 'x1', rows: [{ label: 'y', value: 'y2' }] });
92
+ expect(
93
+ custom([{ datum: null, xValue: 3, yValue: 4 } as never], context),
94
+ ).toMatchObject({
95
+ title: 'x3',
96
+ });
97
+ const up = scene.points.find((p) => (p.datum as { x: number }).x === 0)!;
98
+ expect(
99
+ createChartTooltipContent(
100
+ [up],
101
+ scene,
102
+ false,
103
+ definition.tooltip as unknown as ChartTooltipOptions,
104
+ up,
105
+ ),
106
+ ).toMatchObject({ color: 'green' });
69
107
  });
70
108
 
71
- it('returns an empty array for no candles', () => {
72
- expect(computeCandles([], 12)).toEqual([]);
109
+ it('can drop the tooltip, and clamps the body ratio', () => {
110
+ const definition = candlestickChartDefinition({
111
+ data,
112
+ tooltip: false,
113
+ bodyWidthRatio: 5,
114
+ });
115
+ expect(definition.tooltip).toBe(false);
116
+ expect(render2(definition).found.length).toBeGreaterThan(0);
73
117
  });
74
118
  });