@ledgerhq/lumen-ui-rnative-visualization 0.1.2 → 0.1.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.
- package/dist/module/lib/Components/Axis/Axis.types.js +2 -0
- package/dist/module/lib/Components/Axis/Axis.types.js.map +1 -0
- package/dist/module/lib/Components/Axis/XAxis/XAxis.js +81 -0
- package/dist/module/lib/Components/Axis/XAxis/XAxis.js.map +1 -0
- package/dist/module/lib/Components/Axis/XAxis/XAxis.test.js +121 -0
- package/dist/module/lib/Components/Axis/XAxis/XAxis.test.js.map +1 -0
- package/dist/module/lib/Components/Axis/XAxis/index.js +4 -0
- package/dist/module/lib/Components/Axis/XAxis/index.js.map +1 -0
- package/dist/module/lib/Components/Axis/XAxis/types.js +4 -0
- package/dist/module/lib/Components/Axis/XAxis/types.js.map +1 -0
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js +81 -0
- package/dist/module/lib/Components/Axis/YAxis/YAxis.js.map +1 -0
- package/dist/module/lib/Components/Axis/YAxis/YAxis.test.js +121 -0
- package/dist/module/lib/Components/Axis/YAxis/YAxis.test.js.map +1 -0
- package/dist/module/lib/Components/Axis/YAxis/index.js +4 -0
- package/dist/module/lib/Components/Axis/YAxis/index.js.map +1 -0
- package/dist/module/lib/Components/Axis/YAxis/types.js +4 -0
- package/dist/module/lib/Components/Axis/YAxis/types.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js +123 -0
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/context/cartesianChartContext.js +10 -0
- package/dist/module/lib/Components/CartesianChart/context/cartesianChartContext.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/context/index.js +5 -0
- package/dist/module/lib/Components/CartesianChart/context/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js +75 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js +279 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/index.js +5 -0
- package/dist/module/lib/Components/CartesianChart/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/types.js +4 -0
- package/dist/module/lib/Components/CartesianChart/types.js.map +1 -0
- package/dist/module/lib/Components/Line/Line.js +72 -0
- package/dist/module/lib/Components/Line/Line.js.map +1 -0
- package/dist/module/lib/Components/Line/index.js +4 -0
- package/dist/module/lib/Components/Line/index.js.map +1 -0
- package/dist/module/lib/Components/Line/types.js +2 -0
- package/dist/module/lib/Components/Line/types.js.map +1 -0
- package/dist/module/lib/Components/Line/utils.js +38 -0
- package/dist/module/lib/Components/Line/utils.js.map +1 -0
- package/dist/module/lib/Components/LineChart/LineChart.js +70 -31
- package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.stories.js +271 -2
- package/dist/module/lib/Components/LineChart/LineChart.stories.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.test.js +84 -3
- package/dist/module/lib/Components/LineChart/LineChart.test.js.map +1 -1
- package/dist/module/lib/Components/LineChart/types.js +4 -0
- package/dist/module/lib/Components/LineChart/types.js.map +1 -0
- package/dist/module/lib/utils/domain/domain.js +93 -0
- package/dist/module/lib/utils/domain/domain.js.map +1 -0
- package/dist/module/lib/utils/domain/domain.test.js +152 -0
- package/dist/module/lib/utils/domain/domain.test.js.map +1 -0
- package/dist/module/lib/utils/index.js +0 -2
- package/dist/module/lib/utils/index.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.js +41 -0
- package/dist/module/lib/utils/scales/scales.js.map +1 -0
- package/dist/module/lib/utils/scales/scales.test.js +173 -0
- package/dist/module/lib/utils/scales/scales.test.js.map +1 -0
- package/dist/module/lib/utils/ticks/ticks.js +55 -0
- package/dist/module/lib/utils/ticks/ticks.js.map +1 -0
- package/dist/module/lib/utils/ticks/ticks.test.js +121 -0
- package/dist/module/lib/utils/ticks/ticks.test.js.map +1 -0
- package/dist/module/lib/utils/types.js +2 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts +33 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/XAxis.d.ts +4 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/XAxis.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/types.d.ts +9 -0
- package/dist/typescript/src/lib/Components/Axis/XAxis/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/YAxis.d.ts +4 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/YAxis.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/types.d.ts +14 -0
- package/dist/typescript/src/lib/Components/Axis/YAxis/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/CartesianChart.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/cartesianChartContext.d.ts +8 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/cartesianChartContext.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/useBuildChartContext.d.ts +23 -0
- package/dist/typescript/src/lib/Components/CartesianChart/context/useBuildChartContext.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/index.d.ts +4 -0
- package/dist/typescript/src/lib/Components/CartesianChart/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts +46 -0
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Line/Line.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Line/Line.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Line/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Line/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Line/types.d.ts +24 -0
- package/dist/typescript/src/lib/Components/Line/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Line/utils.d.ts +19 -0
- package/dist/typescript/src/lib/Components/Line/utils.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts +2 -8
- package/dist/typescript/src/lib/Components/LineChart/LineChart.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/index.d.ts +1 -0
- package/dist/typescript/src/lib/Components/LineChart/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts +62 -0
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/utils/domain/domain.d.ts +17 -0
- package/dist/typescript/src/lib/utils/domain/domain.d.ts.map +1 -0
- package/dist/typescript/src/lib/utils/index.d.ts +1 -1
- package/dist/typescript/src/lib/utils/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/scales/scales.d.ts +24 -0
- package/dist/typescript/src/lib/utils/scales/scales.d.ts.map +1 -0
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts +27 -0
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts.map +1 -0
- package/dist/typescript/src/lib/utils/types.d.ts +90 -6
- package/dist/typescript/src/lib/utils/types.d.ts.map +1 -1
- package/package.json +11 -1
- package/src/lib/Components/Axis/Axis.types.ts +32 -0
- package/src/lib/Components/Axis/XAxis/XAxis.test.tsx +89 -0
- package/src/lib/Components/Axis/XAxis/XAxis.tsx +111 -0
- package/src/lib/Components/Axis/XAxis/index.ts +2 -0
- package/src/lib/Components/Axis/XAxis/types.ts +9 -0
- package/src/lib/Components/Axis/YAxis/YAxis.test.tsx +89 -0
- package/src/lib/Components/Axis/YAxis/YAxis.tsx +110 -0
- package/src/lib/Components/Axis/YAxis/index.ts +2 -0
- package/src/lib/Components/Axis/YAxis/types.ts +14 -0
- package/src/lib/Components/CartesianChart/CartesianChart.tsx +153 -0
- package/src/lib/Components/CartesianChart/context/cartesianChartContext.ts +14 -0
- package/src/lib/Components/CartesianChart/context/index.ts +10 -0
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.test.ts +224 -0
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.ts +115 -0
- package/src/lib/Components/CartesianChart/index.ts +3 -0
- package/src/lib/Components/CartesianChart/types.ts +47 -0
- package/src/lib/Components/Line/Line.tsx +88 -0
- package/src/lib/Components/Line/index.ts +2 -0
- package/src/lib/Components/Line/types.ts +23 -0
- package/src/lib/Components/Line/utils.ts +72 -0
- package/src/lib/Components/LineChart/LineChart.stories.tsx +287 -4
- package/src/lib/Components/LineChart/LineChart.test.tsx +71 -2
- package/src/lib/Components/LineChart/LineChart.tsx +90 -34
- package/src/lib/Components/LineChart/index.ts +1 -0
- package/src/lib/Components/LineChart/types.ts +63 -0
- package/src/lib/utils/domain/domain.test.ts +129 -0
- package/src/lib/utils/domain/domain.ts +101 -0
- package/src/lib/utils/index.ts +11 -1
- package/src/lib/utils/scales/scales.test.ts +133 -0
- package/src/lib/utils/scales/scales.ts +67 -0
- package/src/lib/utils/ticks/ticks.test.ts +136 -0
- package/src/lib/utils/ticks/ticks.ts +82 -0
- package/src/lib/utils/types.ts +97 -6
- package/dist/module/lib/utils/math/index.js +0 -4
- package/dist/module/lib/utils/math/index.js.map +0 -1
- package/dist/typescript/src/lib/utils/math/index.d.ts +0 -1
- package/dist/typescript/src/lib/utils/math/index.d.ts.map +0 -1
- package/src/lib/utils/math/index.ts +0 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect } 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
|
+
|
|
6
|
+
import { CartesianChart } from '../../CartesianChart';
|
|
7
|
+
|
|
8
|
+
import type { XAxisProps } from './types';
|
|
9
|
+
import { XAxis } from './XAxis';
|
|
10
|
+
|
|
11
|
+
const sampleSeries = [
|
|
12
|
+
{ id: 'test', stroke: '#000', data: [10, 20, 30, 40, 50] },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const renderXAxis = (props: XAxisProps = {}) =>
|
|
16
|
+
render(
|
|
17
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
18
|
+
<CartesianChart series={sampleSeries} width={400} height={200}>
|
|
19
|
+
<XAxis {...props} />
|
|
20
|
+
</CartesianChart>
|
|
21
|
+
</ThemeProvider>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
describe('XAxis', () => {
|
|
25
|
+
it('renders without crashing', () => {
|
|
26
|
+
const { getByTestId } = renderXAxis();
|
|
27
|
+
getByTestId('chart-svg');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders tick labels for explicit ticks', () => {
|
|
31
|
+
const { getByText } = renderXAxis({ ticks: [0, 2, 4] });
|
|
32
|
+
getByText('0');
|
|
33
|
+
getByText('2');
|
|
34
|
+
getByText('4');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('does not render tick labels for empty ticks', () => {
|
|
38
|
+
const { queryByText } = renderXAxis({ ticks: [] });
|
|
39
|
+
expect(queryByText('0')).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('applies tickLabelFormatter', () => {
|
|
43
|
+
const formatter = (value: number | string) => `v${value}`;
|
|
44
|
+
const { getByText } = renderXAxis({
|
|
45
|
+
ticks: [0, 4],
|
|
46
|
+
tickLabelFormatter: formatter,
|
|
47
|
+
});
|
|
48
|
+
getByText('v0');
|
|
49
|
+
getByText('v4');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renders with showGrid enabled', () => {
|
|
53
|
+
const { toJSON } = renderXAxis({ showGrid: true, ticks: [0, 2] });
|
|
54
|
+
expect(toJSON()).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders with showLine enabled', () => {
|
|
58
|
+
const { toJSON } = renderXAxis({ showLine: true, ticks: [] });
|
|
59
|
+
expect(toJSON()).toBeTruthy();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders with showTickMark enabled', () => {
|
|
63
|
+
const { toJSON } = renderXAxis({ showTickMark: true, ticks: [0, 2] });
|
|
64
|
+
expect(toJSON()).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders with all visual options enabled', () => {
|
|
68
|
+
const { getByText } = renderXAxis({
|
|
69
|
+
showGrid: true,
|
|
70
|
+
showLine: true,
|
|
71
|
+
showTickMark: true,
|
|
72
|
+
ticks: [0, 4],
|
|
73
|
+
});
|
|
74
|
+
getByText('0');
|
|
75
|
+
getByText('4');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not render axis when width is 0', () => {
|
|
79
|
+
const { queryByText } = render(
|
|
80
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
81
|
+
<CartesianChart series={sampleSeries} width={0} height={200}>
|
|
82
|
+
<XAxis ticks={[0, 2, 4]} />
|
|
83
|
+
</CartesianChart>
|
|
84
|
+
</ThemeProvider>,
|
|
85
|
+
);
|
|
86
|
+
expect(queryByText('0')).toBeNull();
|
|
87
|
+
expect(queryByText('2')).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useTheme } from '@ledgerhq/lumen-ui-rnative';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { G, Line as SvgLine, Text as SvgText } from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
import { buildTicksData } from '../../../utils/ticks/ticks';
|
|
6
|
+
import { useCartesianChartContext } from '../../CartesianChart/context';
|
|
7
|
+
|
|
8
|
+
import type { XAxisProps } from './types';
|
|
9
|
+
|
|
10
|
+
const STROKE_WIDTH = 1;
|
|
11
|
+
const TICK_MARK_SIZE = 4;
|
|
12
|
+
const TICK_LABEL_OFFSET = 6;
|
|
13
|
+
export const DEFAULT_AXIS_HEIGHT = 28;
|
|
14
|
+
|
|
15
|
+
export const XAxis = ({
|
|
16
|
+
gridLineStyle = 'dashed',
|
|
17
|
+
position = 'bottom',
|
|
18
|
+
showGrid = false,
|
|
19
|
+
showLine = false,
|
|
20
|
+
showTickMark = false,
|
|
21
|
+
ticks: ticksProp,
|
|
22
|
+
tickLabelFormatter,
|
|
23
|
+
}: XAxisProps) => {
|
|
24
|
+
const { getXScale, getXAxisConfig, drawingArea } = useCartesianChartContext();
|
|
25
|
+
const { theme } = useTheme();
|
|
26
|
+
|
|
27
|
+
const xScale = getXScale();
|
|
28
|
+
const xAxisConfig = getXAxisConfig();
|
|
29
|
+
|
|
30
|
+
const ticksData = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
xScale
|
|
33
|
+
? buildTicksData(xScale, xAxisConfig, ticksProp, tickLabelFormatter)
|
|
34
|
+
: [],
|
|
35
|
+
[xScale, xAxisConfig, ticksProp, tickLabelFormatter],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!xScale || drawingArea.width <= 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isTop = position === 'top';
|
|
43
|
+
const axisY = isTop ? drawingArea.y : drawingArea.y + drawingArea.height;
|
|
44
|
+
|
|
45
|
+
const fontSize = theme.typographies.body4.fontSize;
|
|
46
|
+
const tickDirection = isTop ? -1 : 1;
|
|
47
|
+
const labelY = axisY + tickDirection * (TICK_MARK_SIZE + TICK_LABEL_OFFSET);
|
|
48
|
+
const labelDy = isTop ? 0 : fontSize * 0.8;
|
|
49
|
+
|
|
50
|
+
const gridStroke = theme.colors.border.mutedSubtle;
|
|
51
|
+
const lineStroke = theme.colors.border.muted;
|
|
52
|
+
const textFill = theme.colors.text.muted;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<G>
|
|
56
|
+
{showGrid &&
|
|
57
|
+
ticksData.map((tick, i) => (
|
|
58
|
+
<SvgLine
|
|
59
|
+
key={`grid-${tick.value}-${i}`}
|
|
60
|
+
x1={tick.position}
|
|
61
|
+
y1={drawingArea.y}
|
|
62
|
+
x2={tick.position}
|
|
63
|
+
y2={drawingArea.y + drawingArea.height}
|
|
64
|
+
stroke={gridStroke}
|
|
65
|
+
strokeWidth={STROKE_WIDTH}
|
|
66
|
+
strokeDasharray={gridLineStyle === 'dashed' ? '3 3' : undefined}
|
|
67
|
+
/>
|
|
68
|
+
))}
|
|
69
|
+
|
|
70
|
+
{showLine && (
|
|
71
|
+
<SvgLine
|
|
72
|
+
x1={drawingArea.x}
|
|
73
|
+
y1={axisY}
|
|
74
|
+
x2={drawingArea.x + drawingArea.width}
|
|
75
|
+
y2={axisY}
|
|
76
|
+
stroke={lineStroke}
|
|
77
|
+
strokeWidth={STROKE_WIDTH}
|
|
78
|
+
strokeLinecap='square'
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
{showTickMark &&
|
|
83
|
+
ticksData.map((tick, i) => (
|
|
84
|
+
<SvgLine
|
|
85
|
+
key={`tick-${tick.value}-${i}`}
|
|
86
|
+
x1={tick.position}
|
|
87
|
+
y1={axisY}
|
|
88
|
+
x2={tick.position}
|
|
89
|
+
y2={axisY + tickDirection * TICK_MARK_SIZE}
|
|
90
|
+
stroke={lineStroke}
|
|
91
|
+
strokeWidth={STROKE_WIDTH}
|
|
92
|
+
/>
|
|
93
|
+
))}
|
|
94
|
+
|
|
95
|
+
{ticksData.map((tick, i) => (
|
|
96
|
+
<SvgText
|
|
97
|
+
key={`label-${tick.value}-${i}`}
|
|
98
|
+
x={tick.position}
|
|
99
|
+
y={labelY}
|
|
100
|
+
dy={labelDy}
|
|
101
|
+
textAnchor='middle'
|
|
102
|
+
fill={textFill}
|
|
103
|
+
fontSize={fontSize}
|
|
104
|
+
fontFamily={theme.fontFamilies.sans}
|
|
105
|
+
>
|
|
106
|
+
{tick.label}
|
|
107
|
+
</SvgText>
|
|
108
|
+
))}
|
|
109
|
+
</G>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect } 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
|
+
|
|
6
|
+
import { CartesianChart } from '../../CartesianChart';
|
|
7
|
+
|
|
8
|
+
import type { YAxisProps } from './types';
|
|
9
|
+
import { YAxis } from './YAxis';
|
|
10
|
+
|
|
11
|
+
const sampleSeries = [
|
|
12
|
+
{ id: 'test', stroke: '#000', data: [10, 20, 30, 40, 50] },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const renderYAxis = (props: YAxisProps = {}) =>
|
|
16
|
+
render(
|
|
17
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
18
|
+
<CartesianChart series={sampleSeries} width={400} height={200}>
|
|
19
|
+
<YAxis {...props} />
|
|
20
|
+
</CartesianChart>
|
|
21
|
+
</ThemeProvider>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
describe('YAxis', () => {
|
|
25
|
+
it('renders without crashing', () => {
|
|
26
|
+
const { getByTestId } = renderYAxis();
|
|
27
|
+
getByTestId('chart-svg');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders tick labels for explicit ticks', () => {
|
|
31
|
+
const { getByText } = renderYAxis({ ticks: [10, 30, 50] });
|
|
32
|
+
getByText('10');
|
|
33
|
+
getByText('30');
|
|
34
|
+
getByText('50');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('does not render tick labels for empty ticks', () => {
|
|
38
|
+
const { queryByText } = renderYAxis({ ticks: [] });
|
|
39
|
+
expect(queryByText('10')).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('applies tickLabelFormatter', () => {
|
|
43
|
+
const formatter = (value: number | string) => `$${value}`;
|
|
44
|
+
const { getByText } = renderYAxis({
|
|
45
|
+
ticks: [10, 50],
|
|
46
|
+
tickLabelFormatter: formatter,
|
|
47
|
+
});
|
|
48
|
+
getByText('$10');
|
|
49
|
+
getByText('$50');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renders with showGrid enabled', () => {
|
|
53
|
+
const { toJSON } = renderYAxis({ showGrid: true, ticks: [10, 30] });
|
|
54
|
+
expect(toJSON()).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders with showLine enabled', () => {
|
|
58
|
+
const { toJSON } = renderYAxis({ showLine: true, ticks: [] });
|
|
59
|
+
expect(toJSON()).toBeTruthy();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders with showTickMark enabled', () => {
|
|
63
|
+
const { toJSON } = renderYAxis({ showTickMark: true, ticks: [10, 30] });
|
|
64
|
+
expect(toJSON()).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders with all visual options enabled', () => {
|
|
68
|
+
const { getByText } = renderYAxis({
|
|
69
|
+
showGrid: true,
|
|
70
|
+
showLine: true,
|
|
71
|
+
showTickMark: true,
|
|
72
|
+
ticks: [10, 50],
|
|
73
|
+
});
|
|
74
|
+
getByText('10');
|
|
75
|
+
getByText('50');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not render axis when height is 0', () => {
|
|
79
|
+
const { queryByText } = render(
|
|
80
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='light'>
|
|
81
|
+
<CartesianChart series={sampleSeries} width={400} height={0}>
|
|
82
|
+
<YAxis ticks={[10, 30, 50]} />
|
|
83
|
+
</CartesianChart>
|
|
84
|
+
</ThemeProvider>,
|
|
85
|
+
);
|
|
86
|
+
expect(queryByText('10')).toBeNull();
|
|
87
|
+
expect(queryByText('30')).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useTheme } from '@ledgerhq/lumen-ui-rnative';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { G, Line as SvgLine, Text as SvgText } from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
import { buildTicksData } from '../../../utils/ticks/ticks';
|
|
6
|
+
import { useCartesianChartContext } from '../../CartesianChart/context';
|
|
7
|
+
|
|
8
|
+
import type { YAxisProps } from './types';
|
|
9
|
+
|
|
10
|
+
const STROKE_WIDTH = 1;
|
|
11
|
+
const TICK_MARK_SIZE = 4;
|
|
12
|
+
const TICK_LABEL_OFFSET = 6;
|
|
13
|
+
export const DEFAULT_AXIS_WIDTH = 40;
|
|
14
|
+
|
|
15
|
+
export const YAxis = ({
|
|
16
|
+
position = 'start',
|
|
17
|
+
showGrid = false,
|
|
18
|
+
showLine = false,
|
|
19
|
+
showTickMark = false,
|
|
20
|
+
gridLineStyle = 'dashed',
|
|
21
|
+
ticks: ticksProp,
|
|
22
|
+
tickLabelFormatter,
|
|
23
|
+
}: YAxisProps) => {
|
|
24
|
+
const { getYScale, getYAxisConfig, drawingArea } = useCartesianChartContext();
|
|
25
|
+
const { theme } = useTheme();
|
|
26
|
+
|
|
27
|
+
const yScale = getYScale();
|
|
28
|
+
const yAxisConfig = getYAxisConfig();
|
|
29
|
+
|
|
30
|
+
const ticksData = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
yScale
|
|
33
|
+
? buildTicksData(yScale, yAxisConfig, ticksProp, tickLabelFormatter)
|
|
34
|
+
: [],
|
|
35
|
+
[yScale, yAxisConfig, ticksProp, tickLabelFormatter],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!yScale || drawingArea.height <= 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isStart = position === 'start';
|
|
43
|
+
const axisX = isStart ? drawingArea.x : drawingArea.x + drawingArea.width;
|
|
44
|
+
const tickDirection = isStart ? -1 : 1;
|
|
45
|
+
const labelX = axisX + tickDirection * (TICK_MARK_SIZE + TICK_LABEL_OFFSET);
|
|
46
|
+
const fontSize = theme.typographies.body4.fontSize;
|
|
47
|
+
const labelDy = fontSize * 0.35;
|
|
48
|
+
|
|
49
|
+
const gridStroke = theme.colors.border.mutedSubtle;
|
|
50
|
+
const lineStroke = theme.colors.border.muted;
|
|
51
|
+
const textFill = theme.colors.text.muted;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<G>
|
|
55
|
+
{showGrid &&
|
|
56
|
+
ticksData.map((tick, i) => (
|
|
57
|
+
<SvgLine
|
|
58
|
+
key={`grid-${tick.value}-${i}`}
|
|
59
|
+
x1={drawingArea.x}
|
|
60
|
+
y1={tick.position}
|
|
61
|
+
x2={drawingArea.x + drawingArea.width}
|
|
62
|
+
y2={tick.position}
|
|
63
|
+
stroke={gridStroke}
|
|
64
|
+
strokeWidth={STROKE_WIDTH}
|
|
65
|
+
strokeDasharray={gridLineStyle === 'dashed' ? '3 3' : undefined}
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
|
|
69
|
+
{showLine && (
|
|
70
|
+
<SvgLine
|
|
71
|
+
x1={axisX}
|
|
72
|
+
y1={drawingArea.y}
|
|
73
|
+
x2={axisX}
|
|
74
|
+
y2={drawingArea.y + drawingArea.height}
|
|
75
|
+
stroke={lineStroke}
|
|
76
|
+
strokeWidth={STROKE_WIDTH}
|
|
77
|
+
strokeLinecap='square'
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
80
|
+
|
|
81
|
+
{showTickMark &&
|
|
82
|
+
ticksData.map((tick, i) => (
|
|
83
|
+
<SvgLine
|
|
84
|
+
key={`tick-${tick.value}-${i}`}
|
|
85
|
+
x1={axisX}
|
|
86
|
+
y1={tick.position}
|
|
87
|
+
x2={axisX + tickDirection * TICK_MARK_SIZE}
|
|
88
|
+
y2={tick.position}
|
|
89
|
+
stroke={lineStroke}
|
|
90
|
+
strokeWidth={STROKE_WIDTH}
|
|
91
|
+
/>
|
|
92
|
+
))}
|
|
93
|
+
|
|
94
|
+
{ticksData.map((tick, i) => (
|
|
95
|
+
<SvgText
|
|
96
|
+
key={`label-${tick.value}-${i}`}
|
|
97
|
+
x={labelX}
|
|
98
|
+
y={tick.position}
|
|
99
|
+
dy={labelDy}
|
|
100
|
+
textAnchor={position === 'start' ? 'end' : 'start'}
|
|
101
|
+
fill={textFill}
|
|
102
|
+
fontSize={theme.typographies.body4.fontSize}
|
|
103
|
+
fontFamily={theme.fontFamilies.sans}
|
|
104
|
+
>
|
|
105
|
+
{tick.label}
|
|
106
|
+
</SvgText>
|
|
107
|
+
))}
|
|
108
|
+
</G>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BaseAxisProps } from '../Axis.types';
|
|
2
|
+
|
|
3
|
+
export type YAxisProps = BaseAxisProps & {
|
|
4
|
+
/**
|
|
5
|
+
* Where the y-axis is rendered relative to the drawing area.
|
|
6
|
+
* @default 'start'
|
|
7
|
+
*/
|
|
8
|
+
position?: 'start' | 'end';
|
|
9
|
+
/**
|
|
10
|
+
* Width of the axis in pixels.
|
|
11
|
+
* @default 40
|
|
12
|
+
*/
|
|
13
|
+
width?: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { View, type LayoutChangeEvent } from 'react-native';
|
|
3
|
+
import { Svg } from 'react-native-svg';
|
|
4
|
+
|
|
5
|
+
import type { ChartInset } from '../../utils/types';
|
|
6
|
+
|
|
7
|
+
import { CartesianChartProvider, useBuildChartContext } from './context';
|
|
8
|
+
import type { CartesianChartProps } from './types';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_HEIGHT = 160;
|
|
11
|
+
const DEFAULT_INSET: ChartInset = {
|
|
12
|
+
top: 5,
|
|
13
|
+
right: 20,
|
|
14
|
+
bottom: 0,
|
|
15
|
+
left: 20,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DEFAULT_NEGATIVE_MARGIN = {
|
|
19
|
+
marginLeft: -20,
|
|
20
|
+
marginRight: -20,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const ZERO_PADDING: ChartInset = {
|
|
24
|
+
top: 0,
|
|
25
|
+
right: 0,
|
|
26
|
+
bottom: 0,
|
|
27
|
+
left: 0,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const resolveInset = (inset: CartesianChartProps['inset']): ChartInset => {
|
|
31
|
+
if (inset === undefined) {
|
|
32
|
+
return DEFAULT_INSET;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof inset === 'number') {
|
|
36
|
+
return {
|
|
37
|
+
top: inset + DEFAULT_INSET.top,
|
|
38
|
+
right: inset + DEFAULT_INSET.right,
|
|
39
|
+
bottom: inset + DEFAULT_INSET.bottom,
|
|
40
|
+
left: inset + DEFAULT_INSET.left,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
top: (inset.top ?? 0) + DEFAULT_INSET.top,
|
|
45
|
+
right: (inset.right ?? 0) + DEFAULT_INSET.right,
|
|
46
|
+
bottom: (inset.bottom ?? 0) + DEFAULT_INSET.bottom,
|
|
47
|
+
left: (inset.left ?? 0) + DEFAULT_INSET.left,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const resolveAxisPadding = (
|
|
52
|
+
padding: CartesianChartProps['axisPadding'],
|
|
53
|
+
): ChartInset => {
|
|
54
|
+
if (padding === undefined) {
|
|
55
|
+
return ZERO_PADDING;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
top: padding.top ?? 0,
|
|
60
|
+
right: padding.right ?? 0,
|
|
61
|
+
bottom: padding.bottom ?? 0,
|
|
62
|
+
left: padding.left ?? 0,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const CartesianChart = ({
|
|
67
|
+
series,
|
|
68
|
+
xAxis,
|
|
69
|
+
yAxis,
|
|
70
|
+
width,
|
|
71
|
+
height = DEFAULT_HEIGHT,
|
|
72
|
+
inset,
|
|
73
|
+
axisPadding,
|
|
74
|
+
ariaLabel = 'Chart',
|
|
75
|
+
children,
|
|
76
|
+
}: CartesianChartProps) => {
|
|
77
|
+
const [measuredWidth, setMeasuredWidth] = useState<number | undefined>(width);
|
|
78
|
+
|
|
79
|
+
const needsMeasurement = width === undefined;
|
|
80
|
+
|
|
81
|
+
const handleLayout = useCallback(
|
|
82
|
+
(e: LayoutChangeEvent) => {
|
|
83
|
+
if (needsMeasurement) {
|
|
84
|
+
setMeasuredWidth(e.nativeEvent.layout.width);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
[needsMeasurement],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const resolvedWidth = width ?? measuredWidth ?? 0;
|
|
91
|
+
|
|
92
|
+
const resolvedInset = useMemo(() => resolveInset(inset), [inset]);
|
|
93
|
+
const resolvedAxisPadding = useMemo(
|
|
94
|
+
() => resolveAxisPadding(axisPadding),
|
|
95
|
+
[axisPadding],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const contextValue = useBuildChartContext({
|
|
99
|
+
series,
|
|
100
|
+
xAxis,
|
|
101
|
+
yAxis,
|
|
102
|
+
width: resolvedWidth,
|
|
103
|
+
height,
|
|
104
|
+
inset: resolvedInset,
|
|
105
|
+
axisPadding: resolvedAxisPadding,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const svgContent = resolvedWidth > 0 && (
|
|
109
|
+
<Svg
|
|
110
|
+
testID='chart-svg'
|
|
111
|
+
width={resolvedWidth}
|
|
112
|
+
height={height}
|
|
113
|
+
style={{ overflow: 'visible' }}
|
|
114
|
+
>
|
|
115
|
+
<CartesianChartProvider value={contextValue}>
|
|
116
|
+
{children}
|
|
117
|
+
</CartesianChartProvider>
|
|
118
|
+
</Svg>
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (needsMeasurement) {
|
|
122
|
+
return (
|
|
123
|
+
<View
|
|
124
|
+
testID='chart-container'
|
|
125
|
+
onLayout={handleLayout}
|
|
126
|
+
style={{
|
|
127
|
+
height,
|
|
128
|
+
...DEFAULT_NEGATIVE_MARGIN,
|
|
129
|
+
}}
|
|
130
|
+
accessibilityRole='image'
|
|
131
|
+
accessibilityLabel={ariaLabel}
|
|
132
|
+
>
|
|
133
|
+
{svgContent}
|
|
134
|
+
</View>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<View
|
|
140
|
+
testID='chart-container'
|
|
141
|
+
accessibilityRole='image'
|
|
142
|
+
accessibilityLabel={ariaLabel}
|
|
143
|
+
style={{
|
|
144
|
+
width: resolvedWidth,
|
|
145
|
+
height,
|
|
146
|
+
marginLeft: -20,
|
|
147
|
+
marginRight: -20,
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{svgContent}
|
|
151
|
+
</View>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createSafeContext } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
|
|
3
|
+
import type { CartesianChartContextValue } from '../../../utils/types';
|
|
4
|
+
|
|
5
|
+
const [CartesianChartProvider, _useCartesianChartSafeContext] =
|
|
6
|
+
createSafeContext<CartesianChartContextValue>('CartesianChart');
|
|
7
|
+
|
|
8
|
+
export const useCartesianChartContext = () =>
|
|
9
|
+
_useCartesianChartSafeContext({
|
|
10
|
+
consumerName: 'useCartesianChartContext',
|
|
11
|
+
contextRequired: true,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export { CartesianChartProvider };
|