@ledgerhq/lumen-ui-rnative-visualization 0.1.10 → 0.1.11
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/dist/module/lib/Components/Axis/XAxis/XAxis.js +1 -1
- package/dist/module/lib/Components/Axis/XAxis/XAxis.js.map +1 -1
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js +1 -1
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js +31 -28
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.js +53 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.test.js +144 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/RevealClipDefs.test.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/context.js +8 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/context.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/index.js +5 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/types.js +4 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/types.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/utils.js +32 -0
- package/dist/module/lib/Components/CartesianChart/RevealClip/utils.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/utils.js +4 -4
- package/dist/module/lib/Components/Line/Line.js +5 -2
- package/dist/module/lib/Components/Line/Line.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.js +7 -1
- package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.test.js +25 -0
- package/dist/module/lib/Components/LineChart/LineChart.test.js.map +1 -1
- package/dist/module/lib/Components/Point/Point.js +3 -0
- package/dist/module/lib/Components/Point/Point.js.map +1 -1
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.js +125 -0
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.stories.js +111 -0
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.stories.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.test.js +338 -0
- package/dist/module/lib/Components/ReferenceLine/ReferenceLine.test.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/constants.js +5 -0
- package/dist/module/lib/Components/ReferenceLine/constants.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/index.js +5 -0
- package/dist/module/lib/Components/ReferenceLine/index.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/types.js +2 -0
- package/dist/module/lib/Components/ReferenceLine/types.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/utils.js +121 -0
- package/dist/module/lib/Components/ReferenceLine/utils.js.map +1 -0
- package/dist/module/lib/Components/ReferenceLine/utils.test.js +297 -0
- package/dist/module/lib/Components/ReferenceLine/utils.test.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.js +152 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.stories.js +113 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.stories.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.test.js +132 -0
- package/dist/module/lib/Components/Scrubber/Scrubber.test.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js +105 -0
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.test.js +217 -0
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.test.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/constants.js +10 -0
- package/dist/module/lib/Components/Scrubber/constants.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/context/index.js +4 -0
- package/dist/module/lib/Components/Scrubber/context/index.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/context/scrubberContext.js +10 -0
- package/dist/module/lib/Components/Scrubber/context/scrubberContext.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/index.js +5 -0
- package/dist/module/lib/Components/Scrubber/index.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/types.js +4 -0
- package/dist/module/lib/Components/Scrubber/types.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/utils.js +83 -0
- package/dist/module/lib/Components/Scrubber/utils.js.map +1 -0
- package/dist/module/lib/Components/Scrubber/utils.test.js +192 -0
- package/dist/module/lib/Components/Scrubber/utils.test.js.map +1 -0
- package/dist/module/lib/Components/index.js +2 -0
- package/dist/module/lib/Components/index.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.js +16 -8
- package/dist/module/lib/utils/scales/scales.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.test.js +32 -1
- package/dist/module/lib/utils/scales/scales.test.js.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/context.d.ts +7 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/context.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/types.d.ts +38 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/utils.d.ts +17 -0
- package/dist/typescript/src/lib/Components/CartesianChart/RevealClip/utils.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts +17 -0
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/Line.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts +1 -1
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts +16 -0
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/ReferenceLine/ReferenceLine.d.ts +3 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/ReferenceLine.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/constants.d.ts +3 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/types.d.ts +74 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts +55 -0
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/Scrubber.d.ts +17 -0
- package/dist/typescript/src/lib/Components/Scrubber/Scrubber.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts +16 -0
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/constants.d.ts +8 -0
- package/dist/typescript/src/lib/Components/Scrubber/constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/context/index.d.ts +2 -0
- package/dist/typescript/src/lib/Components/Scrubber/context/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/context/scrubberContext.d.ts +8 -0
- package/dist/typescript/src/lib/Components/Scrubber/context/scrubberContext.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/index.d.ts +4 -0
- package/dist/typescript/src/lib/Components/Scrubber/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts +57 -0
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts +22 -0
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/index.d.ts +2 -0
- package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/scales/scales.d.ts +6 -0
- package/dist/typescript/src/lib/utils/scales/scales.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/lib/Components/Axis/XAxis/XAxis.tsx +1 -1
- package/src/lib/Components/Axis/YAxis/YAxis.tsx +1 -1
- package/src/lib/Components/CartesianChart/CartesianChart.tsx +32 -32
- package/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.test.tsx +129 -0
- package/src/lib/Components/CartesianChart/RevealClip/RevealClipDefs.tsx +59 -0
- package/src/lib/Components/CartesianChart/RevealClip/context.ts +13 -0
- package/src/lib/Components/CartesianChart/RevealClip/index.ts +2 -0
- package/src/lib/Components/CartesianChart/RevealClip/types.ts +38 -0
- package/src/lib/Components/CartesianChart/RevealClip/utils.ts +51 -0
- package/src/lib/Components/CartesianChart/types.ts +17 -0
- package/src/lib/Components/CartesianChart/utils.ts +4 -4
- package/src/lib/Components/Line/Line.tsx +5 -3
- package/src/lib/Components/LineChart/LineChart.test.tsx +23 -0
- package/src/lib/Components/LineChart/LineChart.tsx +6 -0
- package/src/lib/Components/LineChart/types.ts +16 -0
- package/src/lib/Components/Point/Point.tsx +3 -1
- package/src/lib/Components/ReferenceLine/ReferenceLine.stories.tsx +109 -0
- package/src/lib/Components/ReferenceLine/ReferenceLine.test.tsx +269 -0
- package/src/lib/Components/ReferenceLine/ReferenceLine.tsx +149 -0
- package/src/lib/Components/ReferenceLine/constants.ts +2 -0
- package/src/lib/Components/ReferenceLine/index.ts +2 -0
- package/src/lib/Components/ReferenceLine/types.ts +79 -0
- package/src/lib/Components/ReferenceLine/utils.test.ts +332 -0
- package/src/lib/Components/ReferenceLine/utils.ts +175 -0
- package/src/lib/Components/Scrubber/Scrubber.stories.tsx +122 -0
- package/src/lib/Components/Scrubber/Scrubber.test.tsx +110 -0
- package/src/lib/Components/Scrubber/Scrubber.tsx +189 -0
- package/src/lib/Components/Scrubber/ScrubberProvider.test.tsx +223 -0
- package/src/lib/Components/Scrubber/ScrubberProvider.tsx +132 -0
- package/src/lib/Components/Scrubber/constants.ts +7 -0
- package/src/lib/Components/Scrubber/context/index.ts +1 -0
- package/src/lib/Components/Scrubber/context/scrubberContext.ts +14 -0
- package/src/lib/Components/Scrubber/index.ts +3 -0
- package/src/lib/Components/Scrubber/types.ts +59 -0
- package/src/lib/Components/Scrubber/utils.test.ts +172 -0
- package/src/lib/Components/Scrubber/utils.ts +126 -0
- package/src/lib/Components/index.ts +2 -0
- package/src/lib/utils/scales/scales.test.ts +22 -0
- package/src/lib/utils/scales/scales.ts +19 -9
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
|
|
3
|
+
import { ThemeProvider } from '@ledgerhq/lumen-ui-rnative';
|
|
4
|
+
import { render } from '@testing-library/react-native';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { CartesianChart } from '../CartesianChart';
|
|
8
|
+
|
|
9
|
+
import { ReferenceLine } from './ReferenceLine';
|
|
10
|
+
|
|
11
|
+
const sampleSeries = [{ id: 's1', stroke: '#000', data: [10, 20, 30, 40, 50] }];
|
|
12
|
+
|
|
13
|
+
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
14
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
15
|
+
{children}
|
|
16
|
+
</ThemeProvider>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const renderInChart = (
|
|
20
|
+
children: React.ReactNode,
|
|
21
|
+
{
|
|
22
|
+
width = 400,
|
|
23
|
+
height = 200,
|
|
24
|
+
xAxis,
|
|
25
|
+
yAxis,
|
|
26
|
+
}: {
|
|
27
|
+
width?: number;
|
|
28
|
+
height?: number;
|
|
29
|
+
xAxis?: Parameters<typeof CartesianChart>[0]['xAxis'];
|
|
30
|
+
yAxis?: Parameters<typeof CartesianChart>[0]['yAxis'];
|
|
31
|
+
} = {},
|
|
32
|
+
) =>
|
|
33
|
+
render(
|
|
34
|
+
<Wrapper>
|
|
35
|
+
<CartesianChart
|
|
36
|
+
series={sampleSeries}
|
|
37
|
+
width={width}
|
|
38
|
+
height={height}
|
|
39
|
+
xAxis={xAxis}
|
|
40
|
+
yAxis={yAxis}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
</CartesianChart>
|
|
44
|
+
</Wrapper>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
describe('ReferenceLine', () => {
|
|
48
|
+
describe('horizontal line (dataY)', () => {
|
|
49
|
+
it('renders a horizontal line at the correct y-pixel', () => {
|
|
50
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataY={30} />);
|
|
51
|
+
const line = getByTestId('reference-line-line');
|
|
52
|
+
expect(line).toBeTruthy();
|
|
53
|
+
expect(line.props.y1).toBe(line.props.y2);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('spans the full width of the drawing area', () => {
|
|
57
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataY={30} />);
|
|
58
|
+
const line = getByTestId('reference-line-line');
|
|
59
|
+
expect(line.props.x2).toBeGreaterThan(line.props.x1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not render when dataY is outside domain bounds', () => {
|
|
63
|
+
const { queryByTestId } = renderInChart(<ReferenceLine dataY={999} />);
|
|
64
|
+
expect(queryByTestId('reference-line')).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders label with textAnchor start when labelPosition is end', () => {
|
|
68
|
+
const { getByTestId } = renderInChart(
|
|
69
|
+
<ReferenceLine dataY={30} label='Target' labelPosition='end' />,
|
|
70
|
+
);
|
|
71
|
+
const label = getByTestId('reference-line-label');
|
|
72
|
+
expect(label.props.textAnchor).toBe('start');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('renders label with textAnchor end when labelPosition is start', () => {
|
|
76
|
+
const { getByTestId } = renderInChart(
|
|
77
|
+
<ReferenceLine dataY={30} label='Target' labelPosition='start' />,
|
|
78
|
+
);
|
|
79
|
+
const label = getByTestId('reference-line-label');
|
|
80
|
+
expect(label.props.textAnchor).toBe('end');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('renders label with textAnchor middle when labelPosition is center', () => {
|
|
84
|
+
const { getByTestId } = renderInChart(
|
|
85
|
+
<ReferenceLine dataY={30} label='Target' labelPosition='center' />,
|
|
86
|
+
);
|
|
87
|
+
const label = getByTestId('reference-line-label');
|
|
88
|
+
expect(label.props.textAnchor).toBe('middle');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('defaults labelPosition to end', () => {
|
|
92
|
+
const { getByTestId } = renderInChart(
|
|
93
|
+
<ReferenceLine dataY={30} label='Target' />,
|
|
94
|
+
);
|
|
95
|
+
const label = getByTestId('reference-line-label');
|
|
96
|
+
expect(label.props.textAnchor).toBe('start');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('vertical line (dataX)', () => {
|
|
101
|
+
it('renders a vertical line at the correct x-pixel', () => {
|
|
102
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataX={2} />);
|
|
103
|
+
const line = getByTestId('reference-line-line');
|
|
104
|
+
expect(line).toBeTruthy();
|
|
105
|
+
expect(line.props.x1).toBe(line.props.x2);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('spans the full height of the drawing area', () => {
|
|
109
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataX={2} />);
|
|
110
|
+
const line = getByTestId('reference-line-line');
|
|
111
|
+
expect(line.props.y2).toBeGreaterThan(line.props.y1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('does not render when dataX is outside domain bounds', () => {
|
|
115
|
+
const { queryByTestId } = renderInChart(<ReferenceLine dataX={999} />);
|
|
116
|
+
expect(queryByTestId('reference-line')).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('defaults labelPosition to end with dy offset for hanging baseline', () => {
|
|
120
|
+
const { getByTestId } = renderInChart(
|
|
121
|
+
<ReferenceLine dataX={2} label='Event' />,
|
|
122
|
+
);
|
|
123
|
+
const label = getByTestId('reference-line-label');
|
|
124
|
+
expect(label.props.dy).toBe(8);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('works with band (categorical) x-axis scale', () => {
|
|
128
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataX={2} />, {
|
|
129
|
+
xAxis: { scaleType: 'band' },
|
|
130
|
+
});
|
|
131
|
+
expect(getByTestId('reference-line-line')).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('does not render when dataX is outside band scale domain', () => {
|
|
135
|
+
const { queryByTestId } = renderInChart(<ReferenceLine dataX={999} />, {
|
|
136
|
+
xAxis: { scaleType: 'band' },
|
|
137
|
+
});
|
|
138
|
+
expect(queryByTestId('reference-line')).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('resolves dataX through numeric xAxis.data', () => {
|
|
142
|
+
const numericXData = [100, 200, 300, 400, 500];
|
|
143
|
+
const { getByTestId: getIdx0 } = renderInChart(
|
|
144
|
+
<ReferenceLine dataX={0} />,
|
|
145
|
+
{ xAxis: { data: numericXData } },
|
|
146
|
+
);
|
|
147
|
+
const { getByTestId: getIdx2 } = renderInChart(
|
|
148
|
+
<ReferenceLine dataX={2} />,
|
|
149
|
+
{ xAxis: { data: numericXData } },
|
|
150
|
+
);
|
|
151
|
+
const x1Idx0 = getIdx0('reference-line-line').props.x1;
|
|
152
|
+
const x1Idx2 = getIdx2('reference-line-line').props.x1;
|
|
153
|
+
|
|
154
|
+
expect(x1Idx2).toBeGreaterThan(x1Idx0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('does not render when dataX index is out of bounds for xAxis.data', () => {
|
|
158
|
+
const { queryByTestId } = renderInChart(<ReferenceLine dataX={10} />, {
|
|
159
|
+
xAxis: { data: [100, 200, 300] },
|
|
160
|
+
});
|
|
161
|
+
expect(queryByTestId('reference-line')).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('label offsets', () => {
|
|
166
|
+
it('applies labelDx offset to label x position', () => {
|
|
167
|
+
const { getByTestId, unmount } = renderInChart(
|
|
168
|
+
<ReferenceLine dataY={30} label='A' />,
|
|
169
|
+
);
|
|
170
|
+
const xWithoutOffset = getByTestId('reference-line-label').props.x;
|
|
171
|
+
unmount();
|
|
172
|
+
|
|
173
|
+
const { getByTestId: getWithOffset } = renderInChart(
|
|
174
|
+
<ReferenceLine dataY={30} label='A' labelDx={10} />,
|
|
175
|
+
);
|
|
176
|
+
const xWithOffset = getWithOffset('reference-line-label').props.x;
|
|
177
|
+
expect(xWithOffset).toBe(xWithoutOffset + 10);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('applies labelDy offset to label y position', () => {
|
|
181
|
+
const { getByTestId, unmount } = renderInChart(
|
|
182
|
+
<ReferenceLine dataY={30} label='A' />,
|
|
183
|
+
);
|
|
184
|
+
const yWithoutOffset = getByTestId('reference-line-label').props.y;
|
|
185
|
+
unmount();
|
|
186
|
+
|
|
187
|
+
const { getByTestId: getWithOffset } = renderInChart(
|
|
188
|
+
<ReferenceLine dataY={30} label='A' labelDy={-4} />,
|
|
189
|
+
);
|
|
190
|
+
const yWithOffset = getWithOffset('reference-line-label').props.y;
|
|
191
|
+
expect(yWithOffset).toBe(yWithoutOffset - 4);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('styling', () => {
|
|
196
|
+
it('uses dashed line style by default', () => {
|
|
197
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataY={30} />);
|
|
198
|
+
const line = getByTestId('reference-line-line');
|
|
199
|
+
expect(line.props.strokeDasharray).toBe('0.1 6');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('removes dasharray when lineStyle is solid', () => {
|
|
203
|
+
const { getByTestId } = renderInChart(
|
|
204
|
+
<ReferenceLine dataY={30} lineStyle='solid' />,
|
|
205
|
+
);
|
|
206
|
+
const line = getByTestId('reference-line-line');
|
|
207
|
+
expect(line.props.strokeDasharray).toBeUndefined();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('applies custom stroke color', () => {
|
|
211
|
+
const { getByTestId } = renderInChart(
|
|
212
|
+
<ReferenceLine dataY={30} stroke='#FF0000' />,
|
|
213
|
+
);
|
|
214
|
+
const line = getByTestId('reference-line-line');
|
|
215
|
+
expect(line.props.stroke).toBe('#FF0000');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('has round linecap', () => {
|
|
219
|
+
const { getByTestId } = renderInChart(<ReferenceLine dataY={30} />);
|
|
220
|
+
const line = getByTestId('reference-line-line');
|
|
221
|
+
expect(line.props.strokeLinecap).toBe('round');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('label alignment overrides', () => {
|
|
226
|
+
it('applies labelHorizontalAlignment=start on vertical line', () => {
|
|
227
|
+
const { getByTestId } = renderInChart(
|
|
228
|
+
<ReferenceLine
|
|
229
|
+
dataX={2}
|
|
230
|
+
label='Test'
|
|
231
|
+
labelHorizontalAlignment='start'
|
|
232
|
+
/>,
|
|
233
|
+
);
|
|
234
|
+
const label = getByTestId('reference-line-label');
|
|
235
|
+
expect(label.props.textAnchor).toBe('end');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('applies labelVerticalAlignment=start on horizontal line', () => {
|
|
239
|
+
const { getByTestId } = renderInChart(
|
|
240
|
+
<ReferenceLine
|
|
241
|
+
dataY={30}
|
|
242
|
+
label='Test'
|
|
243
|
+
labelVerticalAlignment='start'
|
|
244
|
+
/>,
|
|
245
|
+
);
|
|
246
|
+
const label = getByTestId('reference-line-label');
|
|
247
|
+
expect(label.props.dy).toBe(0);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('applies labelVerticalAlignment=center on horizontal line', () => {
|
|
251
|
+
const { getByTestId } = renderInChart(
|
|
252
|
+
<ReferenceLine
|
|
253
|
+
dataY={30}
|
|
254
|
+
label='Test'
|
|
255
|
+
labelVerticalAlignment='center'
|
|
256
|
+
/>,
|
|
257
|
+
);
|
|
258
|
+
const label = getByTestId('reference-line-label');
|
|
259
|
+
expect(label.props.dy).toBe(3.5);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('no label', () => {
|
|
264
|
+
it('does not render label element when label is not provided', () => {
|
|
265
|
+
const { queryByTestId } = renderInChart(<ReferenceLine dataY={30} />);
|
|
266
|
+
expect(queryByTestId('reference-line-label')).toBeNull();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useTheme } from '@ledgerhq/lumen-ui-rnative';
|
|
2
|
+
import { G, Line as SvgLine, Text as SvgText } from 'react-native-svg';
|
|
3
|
+
|
|
4
|
+
import { useCartesianChartContext } from '../CartesianChart/context';
|
|
5
|
+
|
|
6
|
+
import { DASH_ARRAY, STROKE_WIDTH } from './constants';
|
|
7
|
+
import type { ReferenceLineProps } from './types';
|
|
8
|
+
import {
|
|
9
|
+
computeHorizontalLabelCoordinates,
|
|
10
|
+
computeVerticalLabelCoordinates,
|
|
11
|
+
dominantBaselineToDy,
|
|
12
|
+
resolvePixel,
|
|
13
|
+
} from './utils';
|
|
14
|
+
|
|
15
|
+
export function ReferenceLine({
|
|
16
|
+
label,
|
|
17
|
+
labelDx = 0,
|
|
18
|
+
labelDy = 0,
|
|
19
|
+
labelHorizontalAlignment,
|
|
20
|
+
labelVerticalAlignment,
|
|
21
|
+
labelPosition = 'end',
|
|
22
|
+
stroke,
|
|
23
|
+
lineStyle = 'dashed',
|
|
24
|
+
...props
|
|
25
|
+
}: Readonly<ReferenceLineProps>) {
|
|
26
|
+
const { getXScale, getYScale, getXAxisConfig, getYAxisConfig, drawingArea } =
|
|
27
|
+
useCartesianChartContext();
|
|
28
|
+
const { theme } = useTheme();
|
|
29
|
+
|
|
30
|
+
const resolvedStroke = stroke ?? theme.colors.border.muted;
|
|
31
|
+
const dashArray = lineStyle === 'dashed' ? DASH_ARRAY : undefined;
|
|
32
|
+
const fontSize = theme.typographies.body4.fontSize;
|
|
33
|
+
|
|
34
|
+
if (props.dataY !== undefined) {
|
|
35
|
+
const yPixel = resolvePixel({
|
|
36
|
+
dataValue: props.dataY,
|
|
37
|
+
scale: getYScale(),
|
|
38
|
+
axis: 'y',
|
|
39
|
+
drawingArea,
|
|
40
|
+
axisConfig: getYAxisConfig(),
|
|
41
|
+
});
|
|
42
|
+
if (yPixel === undefined) return null;
|
|
43
|
+
|
|
44
|
+
const labelCoords = label
|
|
45
|
+
? computeHorizontalLabelCoordinates({
|
|
46
|
+
pixel: yPixel,
|
|
47
|
+
labelPosition,
|
|
48
|
+
drawingArea,
|
|
49
|
+
dx: labelDx,
|
|
50
|
+
dy: labelDy,
|
|
51
|
+
horizontalAlignment: labelHorizontalAlignment,
|
|
52
|
+
verticalAlignment: labelVerticalAlignment,
|
|
53
|
+
})
|
|
54
|
+
: null;
|
|
55
|
+
const baselineDy = labelCoords
|
|
56
|
+
? dominantBaselineToDy(labelCoords.dominantBaseline, fontSize)
|
|
57
|
+
: 0;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<G testID='reference-line'>
|
|
61
|
+
<SvgLine
|
|
62
|
+
testID='reference-line-line'
|
|
63
|
+
x1={drawingArea.x}
|
|
64
|
+
y1={yPixel}
|
|
65
|
+
x2={drawingArea.x + drawingArea.width}
|
|
66
|
+
y2={yPixel}
|
|
67
|
+
stroke={resolvedStroke}
|
|
68
|
+
strokeWidth={STROKE_WIDTH}
|
|
69
|
+
strokeDasharray={dashArray}
|
|
70
|
+
strokeLinecap='round'
|
|
71
|
+
/>
|
|
72
|
+
{labelCoords && (
|
|
73
|
+
<SvgText
|
|
74
|
+
testID='reference-line-label'
|
|
75
|
+
x={labelCoords.x}
|
|
76
|
+
y={labelCoords.y}
|
|
77
|
+
dy={baselineDy}
|
|
78
|
+
textAnchor={labelCoords.textAnchor}
|
|
79
|
+
fill={theme.colors.text.muted}
|
|
80
|
+
fontSize={fontSize}
|
|
81
|
+
fontWeight={theme.typographies.body4.fontWeight}
|
|
82
|
+
fontFamily={theme.fontFamilies.sans}
|
|
83
|
+
>
|
|
84
|
+
{label}
|
|
85
|
+
</SvgText>
|
|
86
|
+
)}
|
|
87
|
+
</G>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (props.dataX !== undefined) {
|
|
92
|
+
const xPixel = resolvePixel({
|
|
93
|
+
dataValue: props.dataX,
|
|
94
|
+
scale: getXScale(),
|
|
95
|
+
axis: 'x',
|
|
96
|
+
drawingArea,
|
|
97
|
+
axisConfig: getXAxisConfig(),
|
|
98
|
+
});
|
|
99
|
+
if (xPixel === undefined) return null;
|
|
100
|
+
|
|
101
|
+
const labelCoords = label
|
|
102
|
+
? computeVerticalLabelCoordinates({
|
|
103
|
+
pixel: xPixel,
|
|
104
|
+
labelPosition,
|
|
105
|
+
drawingArea,
|
|
106
|
+
dx: labelDx,
|
|
107
|
+
dy: labelDy,
|
|
108
|
+
horizontalAlignment: labelHorizontalAlignment,
|
|
109
|
+
verticalAlignment: labelVerticalAlignment,
|
|
110
|
+
})
|
|
111
|
+
: null;
|
|
112
|
+
const baselineDy = labelCoords
|
|
113
|
+
? dominantBaselineToDy(labelCoords.dominantBaseline, fontSize)
|
|
114
|
+
: 0;
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<G testID='reference-line'>
|
|
118
|
+
<SvgLine
|
|
119
|
+
testID='reference-line-line'
|
|
120
|
+
x1={xPixel}
|
|
121
|
+
y1={drawingArea.y}
|
|
122
|
+
x2={xPixel}
|
|
123
|
+
y2={drawingArea.y + drawingArea.height}
|
|
124
|
+
stroke={resolvedStroke}
|
|
125
|
+
strokeWidth={STROKE_WIDTH}
|
|
126
|
+
strokeDasharray={dashArray}
|
|
127
|
+
strokeLinecap='round'
|
|
128
|
+
/>
|
|
129
|
+
{labelCoords && (
|
|
130
|
+
<SvgText
|
|
131
|
+
testID='reference-line-label'
|
|
132
|
+
x={labelCoords.x}
|
|
133
|
+
y={labelCoords.y}
|
|
134
|
+
dy={baselineDy}
|
|
135
|
+
textAnchor={labelCoords.textAnchor}
|
|
136
|
+
fill={theme.colors.text.muted}
|
|
137
|
+
fontSize={fontSize}
|
|
138
|
+
fontWeight={theme.typographies.body4.fontWeight}
|
|
139
|
+
fontFamily={theme.fontFamilies.sans}
|
|
140
|
+
>
|
|
141
|
+
{label}
|
|
142
|
+
</SvgText>
|
|
143
|
+
)}
|
|
144
|
+
</G>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type LabelAlignment = 'start' | 'center' | 'end';
|
|
2
|
+
export type LabelPosition = 'start' | 'center' | 'end';
|
|
3
|
+
|
|
4
|
+
export type ReferenceLineBaseProps = {
|
|
5
|
+
/**
|
|
6
|
+
* Label text displayed near the reference line.
|
|
7
|
+
*/
|
|
8
|
+
label?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Horizontal pixel offset applied to the label position.
|
|
11
|
+
* @default 0
|
|
12
|
+
*/
|
|
13
|
+
labelDx?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Vertical pixel offset applied to the label position.
|
|
16
|
+
* @default 0
|
|
17
|
+
*/
|
|
18
|
+
labelDy?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Position of the label along the line.
|
|
21
|
+
* @default 'end'
|
|
22
|
+
*/
|
|
23
|
+
labelPosition?: LabelPosition;
|
|
24
|
+
/**
|
|
25
|
+
* Controls the direction the label text extends from its position.
|
|
26
|
+
* - `'start'`: text extends toward the start of the axis (left)
|
|
27
|
+
* - `'center'`: text is centered on the position
|
|
28
|
+
* - `'end'`: text extends toward the end of the axis (right)
|
|
29
|
+
*
|
|
30
|
+
* Defaults: `'center'` for vertical lines, inferred from `labelPosition` for horizontal lines.
|
|
31
|
+
*/
|
|
32
|
+
labelHorizontalAlignment?: LabelAlignment;
|
|
33
|
+
/**
|
|
34
|
+
* Controls the direction the label text extends from its position.
|
|
35
|
+
* - `'start'`: text extends toward the start of the axis (up)
|
|
36
|
+
* - `'center'`: text is vertically centered on the position
|
|
37
|
+
* - `'end'`: text extends toward the end of the axis (down)
|
|
38
|
+
*
|
|
39
|
+
* Defaults: `'end'` for horizontal lines, inferred from `labelPosition` for vertical lines.
|
|
40
|
+
*/
|
|
41
|
+
labelVerticalAlignment?: LabelAlignment;
|
|
42
|
+
/**
|
|
43
|
+
* Line color.
|
|
44
|
+
* @default theme.colors.border.muted
|
|
45
|
+
*/
|
|
46
|
+
stroke?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Line dash style.
|
|
49
|
+
* - `'dashed'`: rounded dots with `strokeDasharray="0.1 6"`
|
|
50
|
+
* - `'solid'`: continuous line
|
|
51
|
+
* @default 'dashed'
|
|
52
|
+
*/
|
|
53
|
+
lineStyle?: 'solid' | 'dashed';
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type HorizontalReferenceLineProps = ReferenceLineBaseProps & {
|
|
57
|
+
/**
|
|
58
|
+
* Y-axis value in data space where the horizontal line is drawn.
|
|
59
|
+
* The horizontal line is rendered at this Y position spanning the full
|
|
60
|
+
* width of the drawing area.
|
|
61
|
+
*/
|
|
62
|
+
dataY: number;
|
|
63
|
+
dataX?: never;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type VerticalReferenceLineProps = ReferenceLineBaseProps & {
|
|
67
|
+
/**
|
|
68
|
+
* Data index along the X axis. When `xAxis.data` contains numeric values,
|
|
69
|
+
* the index is mapped to the corresponding axis value automatically.
|
|
70
|
+
* The vertical line is drawn at this position spanning the full height
|
|
71
|
+
* of the drawing area.
|
|
72
|
+
*/
|
|
73
|
+
dataX: number;
|
|
74
|
+
dataY?: never;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type ReferenceLineProps =
|
|
78
|
+
| HorizontalReferenceLineProps
|
|
79
|
+
| VerticalReferenceLineProps;
|