@ledgerhq/lumen-ui-rnative-visualization 0.1.14 → 0.1.15
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.constants.js +21 -0
- package/dist/module/lib/Components/Axis/Axis.constants.js.map +1 -0
- package/dist/module/lib/Components/Axis/index.js +5 -0
- package/dist/module/lib/Components/Axis/index.js.map +1 -0
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js +34 -22
- package/dist/module/lib/Components/CartesianChart/CartesianChart.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js +5 -4
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js +54 -0
- package/dist/module/lib/Components/CartesianChart/context/useBuildChartContext.test.js.map +1 -1
- package/dist/module/lib/Components/CartesianChart/utils.js +18 -8
- package/dist/module/lib/Components/CartesianChart/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.js +6 -1
- package/dist/module/lib/Components/Line/utils.js.map +1 -1
- package/dist/module/lib/Components/Line/utils.test.js +88 -0
- package/dist/module/lib/Components/Line/utils.test.js.map +1 -0
- package/dist/module/lib/Components/LineChart/LineChart.js +19 -31
- package/dist/module/lib/Components/LineChart/LineChart.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.stories.js +38 -0
- package/dist/module/lib/Components/LineChart/LineChart.stories.js.map +1 -1
- package/dist/module/lib/Components/LineChart/LineChart.test.js +192 -0
- package/dist/module/lib/Components/LineChart/LineChart.test.js.map +1 -1
- package/dist/module/lib/Components/Point/Point.js +8 -2
- package/dist/module/lib/Components/Point/Point.js.map +1 -1
- package/dist/module/lib/Components/Point/constants.js +9 -0
- package/dist/module/lib/Components/Point/constants.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js +35 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js +153 -0
- package/dist/module/lib/Components/Point/pointContext/MagneticPointsProvider.test.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/index.js +5 -0
- package/dist/module/lib/Components/Point/pointContext/index.js.map +1 -0
- package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js +10 -0
- package/dist/module/lib/Components/Point/pointContext/magneticPointsContext.js.map +1 -0
- package/dist/module/lib/Components/Point/utils.js +26 -6
- package/dist/module/lib/Components/Point/utils.js.map +1 -1
- package/dist/module/lib/Components/Point/utils.test.js +84 -2
- package/dist/module/lib/Components/Point/utils.test.js.map +1 -1
- package/dist/module/lib/Components/ReferenceLine/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/DefaultScrubberTooltip.test.js +4 -4
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.js +31 -10
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.js +4 -4
- package/dist/module/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js +35 -7
- package/dist/module/lib/Components/Scrubber/ScrubberProvider.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/utils.js +64 -0
- package/dist/module/lib/Components/Scrubber/utils.js.map +1 -1
- package/dist/module/lib/Components/Scrubber/utils.test.js +128 -1
- package/dist/module/lib/Components/Scrubber/utils.test.js.map +1 -1
- package/dist/module/lib/utils/domain/domain.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.js +13 -4
- package/dist/module/lib/utils/scales/scales.js.map +1 -1
- package/dist/module/lib/utils/scales/scales.test.js +29 -0
- package/dist/module/lib/utils/scales/scales.test.js.map +1 -1
- package/dist/module/lib/utils/ticks/ticks.js +13 -3
- package/dist/module/lib/utils/ticks/ticks.js.map +1 -1
- package/dist/module/lib/utils/ticks/ticks.test.js +34 -0
- package/dist/module/lib/utils/ticks/ticks.test.js.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/Axis.constants.d.ts +5 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts +39 -0
- package/dist/typescript/src/lib/Components/Axis/Axis.types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Axis/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Axis/index.d.ts.map +1 -0
- 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/context/useBuildChartContext.d.ts +5 -4
- package/dist/typescript/src/lib/Components/CartesianChart/context/useBuildChartContext.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts +11 -3
- package/dist/typescript/src/lib/Components/CartesianChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts +18 -8
- package/dist/typescript/src/lib/Components/CartesianChart/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Line/utils.d.ts +4 -0
- package/dist/typescript/src/lib/Components/Line/utils.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 +10 -3
- package/dist/typescript/src/lib/Components/LineChart/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Point/Point.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/constants.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/constants.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/MagneticPointsProvider.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/MagneticPointsProvider.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts +13 -0
- package/dist/typescript/src/lib/Components/Point/pointContext/magneticPointsContext.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/Point/types.d.ts +7 -0
- package/dist/typescript/src/lib/Components/Point/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Point/utils.d.ts +8 -6
- package/dist/typescript/src/lib/Components/Point/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts +3 -2
- package/dist/typescript/src/lib/Components/ReferenceLine/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.d.ts +5 -1
- package/dist/typescript/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts +13 -0
- package/dist/typescript/src/lib/Components/Scrubber/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts +33 -3
- package/dist/typescript/src/lib/Components/Scrubber/utils.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/domain/domain.d.ts +5 -4
- package/dist/typescript/src/lib/utils/domain/domain.d.ts.map +1 -1
- 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 +11 -5
- package/dist/typescript/src/lib/utils/scales/scales.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts +13 -5
- package/dist/typescript/src/lib/utils/ticks/ticks.d.ts.map +1 -1
- package/dist/typescript/src/lib/utils/types.d.ts +3 -28
- package/dist/typescript/src/lib/utils/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/lib/Components/Axis/Axis.constants.ts +23 -0
- package/src/lib/Components/Axis/Axis.types.ts +40 -0
- package/src/lib/Components/Axis/index.ts +2 -0
- package/src/lib/Components/CartesianChart/CartesianChart.tsx +39 -23
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.test.ts +56 -0
- package/src/lib/Components/CartesianChart/context/useBuildChartContext.ts +8 -8
- package/src/lib/Components/CartesianChart/types.ts +11 -3
- package/src/lib/Components/CartesianChart/utils.ts +18 -8
- package/src/lib/Components/Line/utils.test.ts +90 -0
- package/src/lib/Components/Line/utils.ts +6 -1
- package/src/lib/Components/LineChart/LineChart.stories.tsx +36 -0
- package/src/lib/Components/LineChart/LineChart.test.tsx +156 -0
- package/src/lib/Components/LineChart/LineChart.tsx +30 -40
- package/src/lib/Components/LineChart/types.ts +10 -3
- package/src/lib/Components/Point/Point.tsx +9 -3
- package/src/lib/Components/Point/constants.ts +6 -0
- package/src/lib/Components/Point/pointContext/MagneticPointsProvider.test.tsx +162 -0
- package/src/lib/Components/Point/pointContext/MagneticPointsProvider.tsx +45 -0
- package/src/lib/Components/Point/pointContext/index.ts +5 -0
- package/src/lib/Components/Point/pointContext/magneticPointsContext.ts +19 -0
- package/src/lib/Components/Point/types.ts +7 -0
- package/src/lib/Components/Point/utils.test.ts +95 -5
- package/src/lib/Components/Point/utils.ts +35 -7
- package/src/lib/Components/ReferenceLine/utils.ts +4 -7
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/DefaultScrubberTooltip.test.tsx +4 -4
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.test.ts +4 -4
- package/src/lib/Components/Scrubber/DefaultScrubberTooltip/utils.ts +29 -15
- package/src/lib/Components/Scrubber/ScrubberProvider.tsx +41 -5
- package/src/lib/Components/Scrubber/types.ts +13 -0
- package/src/lib/Components/Scrubber/utils.test.ts +150 -0
- package/src/lib/Components/Scrubber/utils.ts +93 -3
- package/src/lib/utils/domain/domain.ts +6 -5
- package/src/lib/utils/index.ts +0 -2
- package/src/lib/utils/scales/scales.test.ts +19 -0
- package/src/lib/utils/scales/scales.ts +15 -9
- package/src/lib/utils/ticks/ticks.test.ts +45 -0
- package/src/lib/utils/ticks/ticks.ts +19 -9
- package/src/lib/utils/types.ts +3 -26
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BaseAxisProps } from '../../Components/Axis';
|
|
2
|
+
import type { ChartScaleFunction, DrawingArea } from '../types';
|
|
2
3
|
export declare const APPROXIMATE_TICK_COUNT = 5;
|
|
3
4
|
export type TickData = {
|
|
4
5
|
position: number;
|
|
@@ -7,9 +8,16 @@ export type TickData = {
|
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
9
10
|
* Resolves which numeric tick values should appear on the axis.
|
|
10
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* Priority:
|
|
13
|
+
* 1. Explicit `ticks` provided by the consumer.
|
|
14
|
+
* 2. Axis `data` — when provided, ticks come from the data itself (numeric
|
|
15
|
+
* values for numeric data, indices for string data) so the rendered ticks
|
|
16
|
+
* mirror exactly what the consumer asked for, with no d3-invented
|
|
17
|
+
* intermediate values.
|
|
18
|
+
* 3. Scale-specific defaults (band domain, or `scale.ticks()` for numeric).
|
|
11
19
|
*/
|
|
12
|
-
export declare const getTickValues: (scale: ChartScaleFunction, explicitTicks?: number[]) => number[];
|
|
20
|
+
export declare const getTickValues: (scale: ChartScaleFunction, explicitTicks?: number[], axisData?: BaseAxisProps["data"]) => number[];
|
|
13
21
|
/**
|
|
14
22
|
* Converts a tick value to its pixel position on the axis.
|
|
15
23
|
* Band scales are centered within the band.
|
|
@@ -19,11 +27,11 @@ export declare const getTickPosition: (scale: ChartScaleFunction, tick: number)
|
|
|
19
27
|
* Resolves the display label for a tick value.
|
|
20
28
|
* Priority: formatter > string label lookup > raw value.
|
|
21
29
|
*/
|
|
22
|
-
export declare const getTickLabel: (tick: number, axisData:
|
|
30
|
+
export declare const getTickLabel: (tick: number, axisData: BaseAxisProps["data"], formatter?: (value: number | string) => string) => string;
|
|
23
31
|
/**
|
|
24
32
|
* Builds the complete tick data array from a scale and axis configuration.
|
|
25
33
|
*/
|
|
26
|
-
export declare const buildTicksData: (scale: ChartScaleFunction, axisConfig?:
|
|
34
|
+
export declare const buildTicksData: (scale: ChartScaleFunction, axisConfig?: BaseAxisProps, explicitTicks?: number[], formatter?: (value: number | string) => string) => TickData[];
|
|
27
35
|
/**
|
|
28
36
|
* Excludes the X grid line at the Y-axis origin (left edge)
|
|
29
37
|
* to prevent overlap with the Y-axis solid line.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticks.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/utils/ticks/ticks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ticks.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/utils/ticks/ticks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEhE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,kBAAkB,EACzB,gBAAgB,MAAM,EAAE,EACxB,WAAW,aAAa,CAAC,MAAM,CAAC,KAC/B,MAAM,EAUR,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,kBAAkB,EACzB,MAAM,MAAM,KACX,MAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,UAAU,aAAa,CAAC,MAAM,CAAC,EAC/B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,KAC7C,MAeF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,kBAAkB,EACzB,aAAa,aAAa,EAC1B,gBAAgB,MAAM,EAAE,EACxB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,KAC7C,QAAQ,EASV,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,QAAQ,EACd,aAAa,WAAW,KACvB,OAEF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,QAAQ,EACd,aAAa,WAAW,KACvB,OAEF,CAAC"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { ScaleBand, ScaleLinear, ScaleLogarithmic } from 'd3-scale';
|
|
2
|
-
|
|
3
|
-
min: number;
|
|
4
|
-
max: number;
|
|
5
|
-
};
|
|
2
|
+
import type { BaseAxisProps } from '../Components/Axis';
|
|
6
3
|
export type ChartInset = {
|
|
7
4
|
top: number;
|
|
8
5
|
right: number;
|
|
@@ -36,28 +33,6 @@ export type Series = {
|
|
|
36
33
|
*/
|
|
37
34
|
stroke: string;
|
|
38
35
|
};
|
|
39
|
-
export type AxisConfigProps = {
|
|
40
|
-
/**
|
|
41
|
-
* Scale algorithm used by this axis.
|
|
42
|
-
* @default 'linear'
|
|
43
|
-
*/
|
|
44
|
-
scaleType?: 'linear' | 'log' | 'band';
|
|
45
|
-
/**
|
|
46
|
-
* Explicit data values for band scales or category labels.
|
|
47
|
-
* For band scales, provides the discrete domain. For numeric scales, string values
|
|
48
|
-
* are used as tick labels at corresponding indices.
|
|
49
|
-
*/
|
|
50
|
-
data?: string[] | number[];
|
|
51
|
-
/**
|
|
52
|
-
* Fixed domain bounds or a function that adjusts the computed bounds.
|
|
53
|
-
* A partial object overrides only the specified bound(s).
|
|
54
|
-
* A function receives the auto-computed bounds and returns adjusted ones.
|
|
55
|
-
*
|
|
56
|
-
* The final domain is always rounded to nice boundaries via d3's `.nice()`,
|
|
57
|
-
* ensuring tick marks land on clean values (e.g. `[4, 98]` becomes `[0, 100]`).
|
|
58
|
-
*/
|
|
59
|
-
domain?: Partial<AxisBounds> | ((bounds: AxisBounds) => AxisBounds);
|
|
60
|
-
};
|
|
61
36
|
export type NumericScale = ScaleLinear<number, number> | ScaleLogarithmic<number, number>;
|
|
62
37
|
export type CategoricalScale = ScaleBand<number>;
|
|
63
38
|
export type ChartScaleFunction = NumericScale | CategoricalScale;
|
|
@@ -81,11 +56,11 @@ export type CartesianChartContextValue = {
|
|
|
81
56
|
/**
|
|
82
57
|
* Returns the x-axis config. Accepts an optional axis ID for future multi-axis support.
|
|
83
58
|
*/
|
|
84
|
-
getXAxisConfig: (id?: string) =>
|
|
59
|
+
getXAxisConfig: (id?: string) => BaseAxisProps | undefined;
|
|
85
60
|
/**
|
|
86
61
|
* Returns the y-axis config. Accepts an optional axis ID for future multi-axis support.
|
|
87
62
|
*/
|
|
88
|
-
getYAxisConfig: (id?: string) =>
|
|
63
|
+
getYAxisConfig: (id?: string) => BaseAxisProps | undefined;
|
|
89
64
|
/**
|
|
90
65
|
* Pixel bounds of the drawable region.
|
|
91
66
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/utils/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEzE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/utils/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACzB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErC,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEjE,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;OAEG;IACH,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IAC3D;;OAEG;IACH,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IAC3D;;OAEG;IACH,cAAc,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;IAC3D;;OAEG;IACH,cAAc,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;IAC3D;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/lumen-ui-rnative-visualization",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"keywords": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"!**/*.tsbuildinfo"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ledgerhq/lumen-utils-shared": "0.1.
|
|
34
|
+
"@ledgerhq/lumen-utils-shared": "0.1.5",
|
|
35
35
|
"d3-array": "^3.2.4",
|
|
36
36
|
"d3-scale": "^4.0.2",
|
|
37
37
|
"d3-shape": "^3.2.0"
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@types/react": "^19.0.0",
|
|
47
|
-
"@ledgerhq/lumen-design-core": "0.1.
|
|
48
|
-
"@ledgerhq/lumen-ui-rnative": "0.1.
|
|
47
|
+
"@ledgerhq/lumen-design-core": "0.1.16",
|
|
48
|
+
"@ledgerhq/lumen-ui-rnative": "0.1.38",
|
|
49
49
|
"react": "^19.0.0",
|
|
50
50
|
"react-native": "~0.79.7",
|
|
51
51
|
"react-native-svg": "^15.0.0",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { XAxisProps } from './XAxis';
|
|
2
|
+
import { DEFAULT_AXIS_WIDTH } from './YAxis';
|
|
3
|
+
|
|
4
|
+
import type { YAxisProps } from './YAxis';
|
|
5
|
+
|
|
6
|
+
export const defaultXAxisProps: XAxisProps = {
|
|
7
|
+
position: 'bottom',
|
|
8
|
+
showGrid: false,
|
|
9
|
+
showLine: false,
|
|
10
|
+
showTickMark: false,
|
|
11
|
+
scaleType: 'linear',
|
|
12
|
+
nice: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const defaultYAxisProps: YAxisProps = {
|
|
16
|
+
position: 'start',
|
|
17
|
+
showGrid: false,
|
|
18
|
+
showLine: false,
|
|
19
|
+
showTickMark: false,
|
|
20
|
+
scaleType: 'linear',
|
|
21
|
+
nice: true,
|
|
22
|
+
width: DEFAULT_AXIS_WIDTH,
|
|
23
|
+
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export type AxisBounds = {
|
|
2
|
+
min: number;
|
|
3
|
+
max: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
export type BaseAxisProps = {
|
|
2
7
|
/**
|
|
3
8
|
* Whether to render grid lines at each tick.
|
|
@@ -29,4 +34,39 @@ export type BaseAxisProps = {
|
|
|
29
34
|
* Receives the raw tick value (number or string label) and must return a string.
|
|
30
35
|
*/
|
|
31
36
|
tickLabelFormatter?: (value: number | string) => string;
|
|
37
|
+
/**
|
|
38
|
+
* Scale algorithm used by this axis.
|
|
39
|
+
* @default 'linear'
|
|
40
|
+
*/
|
|
41
|
+
scaleType?: 'linear' | 'log' | 'band';
|
|
42
|
+
/**
|
|
43
|
+
* Explicit data values for band scales or category labels.
|
|
44
|
+
* For band scales, provides the discrete domain. For numeric scales, string values
|
|
45
|
+
* are used as tick labels at corresponding indices.
|
|
46
|
+
*/
|
|
47
|
+
data?: string[] | number[];
|
|
48
|
+
/**
|
|
49
|
+
* Fixed domain bounds or a function that adjusts the computed bounds.
|
|
50
|
+
* A partial object overrides only the specified bound(s).
|
|
51
|
+
* A function receives the auto-computed bounds and returns adjusted ones.
|
|
52
|
+
*
|
|
53
|
+
* Applied before {@link BaseAxisProps.nice}. To keep your bounds exactly as
|
|
54
|
+
* provided, set `nice: false` alongside a full `{ min, max }` override.
|
|
55
|
+
*/
|
|
56
|
+
domain?: Partial<AxisBounds> | ((bounds: AxisBounds) => AxisBounds);
|
|
57
|
+
/**
|
|
58
|
+
* Round the domain outward to clean boundaries via d3's `.nice()`
|
|
59
|
+
* (e.g. `[4, 98]` becomes `[0, 100]`).
|
|
60
|
+
*
|
|
61
|
+
* Defaults to `true` for Y Axis and `false` for X Axis.
|
|
62
|
+
*
|
|
63
|
+
* Set to `false` to keep the domain exactly as provided
|
|
64
|
+
* so data fills the plot area boundary-to-boundary — typically only useful
|
|
65
|
+
* when you also pass a full `domain: { min, max }` and don't have overlays
|
|
66
|
+
* (reference lines, scrubber positions, annotations) that may sit outside
|
|
67
|
+
* the data range.
|
|
68
|
+
*
|
|
69
|
+
* Applied after any {@link BaseAxisProps.domain} override.
|
|
70
|
+
*/
|
|
71
|
+
nice?: boolean;
|
|
32
72
|
};
|
|
@@ -2,13 +2,15 @@ import { useCallback, useMemo, useState } from 'react';
|
|
|
2
2
|
import { View, type LayoutChangeEvent } from 'react-native';
|
|
3
3
|
import { Svg } from 'react-native-svg';
|
|
4
4
|
|
|
5
|
+
import { MagneticPointsProvider } from '../Point/pointContext';
|
|
5
6
|
import { ScrubberProvider } from '../Scrubber/ScrubberProvider';
|
|
6
7
|
import { CartesianChartProvider, useBuildChartContext } from './context';
|
|
7
8
|
import { RevealClipDefs } from './RevealClip';
|
|
8
9
|
import type { CartesianChartProps } from './types';
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_HEIGHT,
|
|
11
|
-
|
|
12
|
+
OVERFLOW_BUFFER,
|
|
13
|
+
OVERFLOW_OFFSET,
|
|
12
14
|
resolveAxisPadding,
|
|
13
15
|
resolveInset,
|
|
14
16
|
} from './utils';
|
|
@@ -26,6 +28,7 @@ export function CartesianChart({
|
|
|
26
28
|
enableScrubbing = false,
|
|
27
29
|
onScrubberPositionChange,
|
|
28
30
|
animate = true,
|
|
31
|
+
magnetRadius,
|
|
29
32
|
}: Readonly<CartesianChartProps>) {
|
|
30
33
|
const [measuredWidth, setMeasuredWidth] = useState<number | undefined>(width);
|
|
31
34
|
|
|
@@ -41,6 +44,15 @@ export function CartesianChart({
|
|
|
41
44
|
|
|
42
45
|
const resolvedWidth = width ?? measuredWidth ?? 0;
|
|
43
46
|
|
|
47
|
+
// The SVG canvas is enlarged by the overflow buffer on every side so edge
|
|
48
|
+
// content (labels, points, ticks) is not clipped, then shifted back by
|
|
49
|
+
// `OVERFLOW_OFFSET` so the drawing area still spans the container footprint.
|
|
50
|
+
const svgWidth =
|
|
51
|
+
resolvedWidth > 0
|
|
52
|
+
? resolvedWidth + OVERFLOW_BUFFER.left + OVERFLOW_BUFFER.right
|
|
53
|
+
: 0;
|
|
54
|
+
const svgHeight = height + OVERFLOW_BUFFER.top + OVERFLOW_BUFFER.bottom;
|
|
55
|
+
|
|
44
56
|
const resolvedInset = useMemo(() => resolveInset(inset), [inset]);
|
|
45
57
|
const resolvedAxisPadding = useMemo(
|
|
46
58
|
() => resolveAxisPadding(axisPadding),
|
|
@@ -51,8 +63,8 @@ export function CartesianChart({
|
|
|
51
63
|
series,
|
|
52
64
|
xAxis,
|
|
53
65
|
yAxis,
|
|
54
|
-
width:
|
|
55
|
-
height,
|
|
66
|
+
width: svgWidth,
|
|
67
|
+
height: svgHeight,
|
|
56
68
|
inset: resolvedInset,
|
|
57
69
|
axisPadding: resolvedAxisPadding,
|
|
58
70
|
});
|
|
@@ -66,32 +78,36 @@ export function CartesianChart({
|
|
|
66
78
|
style={{
|
|
67
79
|
width: needsMeasurement ? undefined : resolvedWidth,
|
|
68
80
|
height,
|
|
69
|
-
|
|
81
|
+
overflow: 'visible',
|
|
70
82
|
}}
|
|
71
83
|
>
|
|
72
84
|
{resolvedWidth > 0 && (
|
|
73
85
|
<CartesianChartProvider value={contextValue}>
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
width={resolvedWidth}
|
|
83
|
-
height={height}
|
|
84
|
-
style={{ overflow: 'visible' }}
|
|
86
|
+
<MagneticPointsProvider>
|
|
87
|
+
<ScrubberProvider
|
|
88
|
+
width={svgWidth}
|
|
89
|
+
height={svgHeight}
|
|
90
|
+
enableScrubbing={enableScrubbing}
|
|
91
|
+
onScrubberPositionChange={onScrubberPositionChange}
|
|
92
|
+
style={OVERFLOW_OFFSET}
|
|
93
|
+
magnetRadius={magnetRadius}
|
|
85
94
|
>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
<Svg
|
|
96
|
+
testID='chart-svg'
|
|
97
|
+
width={svgWidth}
|
|
98
|
+
height={svgHeight}
|
|
99
|
+
style={{ overflow: 'visible' }}
|
|
90
100
|
>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
<RevealClipDefs
|
|
102
|
+
drawingArea={contextValue.drawingArea}
|
|
103
|
+
series={series}
|
|
104
|
+
animate={animate}
|
|
105
|
+
>
|
|
106
|
+
{children}
|
|
107
|
+
</RevealClipDefs>
|
|
108
|
+
</Svg>
|
|
109
|
+
</ScrubberProvider>
|
|
110
|
+
</MagneticPointsProvider>
|
|
95
111
|
</CartesianChartProvider>
|
|
96
112
|
)}
|
|
97
113
|
</View>
|
|
@@ -221,4 +221,60 @@ describe('useBuildChartContext', () => {
|
|
|
221
221
|
expect(result.current.drawingArea.width).toBe(400 - 40);
|
|
222
222
|
expect(result.current.drawingArea.height).toBe(200 - 28);
|
|
223
223
|
});
|
|
224
|
+
|
|
225
|
+
describe('nice domain prop', () => {
|
|
226
|
+
// Series of length 3770 → auto X domain is [0, 3769]; `.nice()` rounds it
|
|
227
|
+
// outward to [0, 4000] (the trailing "dead zone" keeps overlays that sit
|
|
228
|
+
// outside the data range — e.g. reference lines — from being clipped).
|
|
229
|
+
const longSeries = makeSeries(Array.from({ length: 3770 }, (_, i) => i));
|
|
230
|
+
|
|
231
|
+
it('rounds the X domain by default (.nice())', () => {
|
|
232
|
+
const { result } = renderHook(() =>
|
|
233
|
+
useBuildChartContext(hookParams({ series: [longSeries] })),
|
|
234
|
+
);
|
|
235
|
+
expect(result.current.getXScale()!.domain()).toEqual([0, 4000]);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('rounds the Y domain by default (.nice())', () => {
|
|
239
|
+
const { result } = renderHook(() =>
|
|
240
|
+
useBuildChartContext(
|
|
241
|
+
hookParams({
|
|
242
|
+
yAxis: {
|
|
243
|
+
domain: (bounds: { min: number; max: number }) => ({
|
|
244
|
+
min: bounds.min - 5,
|
|
245
|
+
max: bounds.max + 5,
|
|
246
|
+
}),
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
expect(result.current.getYScale()!.domain()).toEqual([0, 40]);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('opts the X axis out of .nice() when nice is false', () => {
|
|
255
|
+
const { result } = renderHook(() =>
|
|
256
|
+
useBuildChartContext(
|
|
257
|
+
hookParams({ series: [longSeries], xAxis: { nice: false } }),
|
|
258
|
+
),
|
|
259
|
+
);
|
|
260
|
+
expect(result.current.getXScale()!.domain()).toEqual([0, 3769]);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('opts the Y axis out of .nice() when nice is false', () => {
|
|
264
|
+
const { result } = renderHook(() =>
|
|
265
|
+
useBuildChartContext(
|
|
266
|
+
hookParams({
|
|
267
|
+
yAxis: {
|
|
268
|
+
nice: false,
|
|
269
|
+
domain: (bounds: { min: number; max: number }) => ({
|
|
270
|
+
min: bounds.min - 5,
|
|
271
|
+
max: bounds.max + 5,
|
|
272
|
+
}),
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
275
|
+
),
|
|
276
|
+
);
|
|
277
|
+
expect(result.current.getYScale()!.domain()).toEqual([5, 35]);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
224
280
|
});
|
|
@@ -11,19 +11,18 @@ import {
|
|
|
11
11
|
isBandScaleType,
|
|
12
12
|
} from '../../../utils/scales/scales';
|
|
13
13
|
import type {
|
|
14
|
-
AxisBounds,
|
|
15
|
-
AxisConfigProps,
|
|
16
14
|
CartesianChartContextValue,
|
|
17
15
|
ChartInset,
|
|
18
16
|
ChartScaleFunction,
|
|
19
17
|
DrawingArea,
|
|
20
18
|
Series,
|
|
21
19
|
} from '../../../utils/types';
|
|
20
|
+
import type { AxisBounds, BaseAxisProps } from '../../Axis';
|
|
22
21
|
|
|
23
22
|
type UseBuildChartContextParams = {
|
|
24
23
|
series: Series[];
|
|
25
|
-
xAxis?: Partial<
|
|
26
|
-
yAxis?: Partial<
|
|
24
|
+
xAxis?: Partial<BaseAxisProps>;
|
|
25
|
+
yAxis?: Partial<BaseAxisProps>;
|
|
27
26
|
width: number;
|
|
28
27
|
height: number;
|
|
29
28
|
inset: ChartInset;
|
|
@@ -45,11 +44,12 @@ export const computeAxisRange = (
|
|
|
45
44
|
export const buildScale = (
|
|
46
45
|
domain: AxisBounds,
|
|
47
46
|
range: AxisRange,
|
|
48
|
-
scaleType:
|
|
47
|
+
scaleType: BaseAxisProps['scaleType'] = 'linear',
|
|
48
|
+
nice?: boolean,
|
|
49
49
|
): ChartScaleFunction =>
|
|
50
50
|
isBandScaleType(scaleType)
|
|
51
51
|
? getCategoricalScale({ domain, range })
|
|
52
|
-
: getNumericScale({ scaleType, domain, range });
|
|
52
|
+
: getNumericScale({ scaleType, domain, range, nice });
|
|
53
53
|
|
|
54
54
|
export const computeDrawingArea = (
|
|
55
55
|
width: number,
|
|
@@ -94,8 +94,8 @@ export const useBuildChartContext = ({
|
|
|
94
94
|
if (drawingArea.width > 0 && drawingArea.height > 0) {
|
|
95
95
|
const xDomain = computeXDomain(series, xAxis);
|
|
96
96
|
const yDomain = computeYDomain(series, yAxis);
|
|
97
|
-
xScale = buildScale(xDomain, xRange, xAxis?.scaleType);
|
|
98
|
-
yScale = buildScale(yDomain, yRange, yAxis?.scaleType);
|
|
97
|
+
xScale = buildScale(xDomain, xRange, xAxis?.scaleType, xAxis?.nice);
|
|
98
|
+
yScale = buildScale(yDomain, yRange, yAxis?.scaleType, yAxis?.nice);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const dataLength = computeDataLength(series, xAxis);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { ChartInset, Series } from '../../utils/types';
|
|
4
|
+
import type { BaseAxisProps } from '../Axis';
|
|
4
5
|
|
|
5
6
|
export type CartesianChartProps = {
|
|
6
7
|
/**
|
|
@@ -10,11 +11,11 @@ export type CartesianChartProps = {
|
|
|
10
11
|
/**
|
|
11
12
|
* Scale and domain configuration for the x-axis.
|
|
12
13
|
*/
|
|
13
|
-
xAxis?: Partial<
|
|
14
|
+
xAxis?: Partial<BaseAxisProps>;
|
|
14
15
|
/**
|
|
15
16
|
* Scale and domain configuration for the y-axis.
|
|
16
17
|
*/
|
|
17
|
-
yAxis?: Partial<
|
|
18
|
+
yAxis?: Partial<BaseAxisProps>;
|
|
18
19
|
/**
|
|
19
20
|
* Width of the chart in pixels.
|
|
20
21
|
* When omitted, the component auto-measures via `onLayout`.
|
|
@@ -63,4 +64,11 @@ export type CartesianChartProps = {
|
|
|
63
64
|
* @default true
|
|
64
65
|
*/
|
|
65
66
|
animate?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Pixel radius within which the scrubber magnetically snaps to registered
|
|
69
|
+
* magnetic `<Point>` components. Requires `enableScrubbing` to be `true`.
|
|
70
|
+
* Set to `0` to disable magnetisation.
|
|
71
|
+
* @default 6
|
|
72
|
+
*/
|
|
73
|
+
magnetRadius?: number;
|
|
66
74
|
};
|
|
@@ -4,9 +4,11 @@ import type { CartesianChartProps } from './types';
|
|
|
4
4
|
export const DEFAULT_HEIGHT = 160;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Internal buffer
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Internal buffer added around the drawing area so SVG content (labels, points,
|
|
8
|
+
* ticks) drawn at the edges is not clipped. The SVG canvas is enlarged by this
|
|
9
|
+
* buffer on every side and shifted back by {@link OVERFLOW_OFFSET} so
|
|
10
|
+
* the chart's layout footprint (and the drawing area) stays exactly the
|
|
11
|
+
* consumer-provided width/height.
|
|
10
12
|
*/
|
|
11
13
|
export const OVERFLOW_BUFFER: ChartInset = {
|
|
12
14
|
top: 50,
|
|
@@ -14,11 +16,19 @@ export const OVERFLOW_BUFFER: ChartInset = {
|
|
|
14
16
|
bottom: 50,
|
|
15
17
|
left: 50,
|
|
16
18
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Top/left offset applied to the (enlarged) SVG group so its drawing area aligns
|
|
21
|
+
* with the container's top-left. The extra width/height added to the SVG
|
|
22
|
+
* overflows symmetrically on every side via `overflow: visible`.
|
|
23
|
+
*
|
|
24
|
+
* Uses `position: relative` to keep parity with the web implementation, where a
|
|
25
|
+
* negative `margin-top` would collapse through the container instead of
|
|
26
|
+
* offsetting the SVG inside it.
|
|
27
|
+
*/
|
|
28
|
+
export const OVERFLOW_OFFSET = {
|
|
29
|
+
position: 'relative' as const,
|
|
30
|
+
top: -OVERFLOW_BUFFER.top,
|
|
31
|
+
left: -OVERFLOW_BUFFER.left,
|
|
22
32
|
};
|
|
23
33
|
export const ZERO_PADDING: ChartInset = {
|
|
24
34
|
top: 0,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getCategoricalScale,
|
|
5
|
+
getNumericScale,
|
|
6
|
+
} from '../../utils/scales/scales';
|
|
7
|
+
|
|
8
|
+
import { toScaledPoints } from './utils';
|
|
9
|
+
|
|
10
|
+
const xScale = getNumericScale({
|
|
11
|
+
scaleType: 'linear',
|
|
12
|
+
domain: { min: 0, max: 7 },
|
|
13
|
+
range: { min: 0, max: 800 },
|
|
14
|
+
nice: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const yScale = getNumericScale({
|
|
18
|
+
scaleType: 'linear',
|
|
19
|
+
domain: { min: 0, max: 100 },
|
|
20
|
+
range: { min: 200, max: 0 },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('toScaledPoints', () => {
|
|
24
|
+
it('uses index as x input when no xData is provided', () => {
|
|
25
|
+
const data = [10, 50, 90];
|
|
26
|
+
const points = toScaledPoints(data, xScale, yScale);
|
|
27
|
+
|
|
28
|
+
expect(points).not.toBeNull();
|
|
29
|
+
expect(points).toHaveLength(3);
|
|
30
|
+
expect(points![0][0]).toBe(0);
|
|
31
|
+
expect(points![1][0]).toBeCloseTo(800 / 7);
|
|
32
|
+
expect(points![2][0]).toBeCloseTo((800 / 7) * 2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('caps iteration to xData.length when series is longer', () => {
|
|
36
|
+
const data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
|
|
37
|
+
const xData = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug'];
|
|
38
|
+
|
|
39
|
+
const points = toScaledPoints(data, xScale, yScale, xData);
|
|
40
|
+
|
|
41
|
+
expect(points).not.toBeNull();
|
|
42
|
+
expect(points).toHaveLength(xData.length);
|
|
43
|
+
expect(points![0][0]).toBe(0);
|
|
44
|
+
expect(points![points!.length - 1][0]).toBe(800);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('skips null values without affecting the cap', () => {
|
|
48
|
+
const data: (number | null)[] = [10, null, 30, 40, 50, 60, 70, 80, 90];
|
|
49
|
+
const xData = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
|
50
|
+
|
|
51
|
+
const points = toScaledPoints(data, xScale, yScale, xData);
|
|
52
|
+
|
|
53
|
+
expect(points).not.toBeNull();
|
|
54
|
+
expect(points).toHaveLength(7);
|
|
55
|
+
expect(points![points!.length - 1][0]).toBe((800 / 7) * 7);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('uses numeric xData values as the x input', () => {
|
|
59
|
+
const data = [10, 20, 30];
|
|
60
|
+
const customX = [0, 3.5, 7];
|
|
61
|
+
|
|
62
|
+
const points = toScaledPoints(data, xScale, yScale, customX);
|
|
63
|
+
|
|
64
|
+
expect(points![0][0]).toBe(0);
|
|
65
|
+
expect(points![1][0]).toBeCloseTo(400);
|
|
66
|
+
expect(points![2][0]).toBe(800);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('centers points within categorical band scales', () => {
|
|
70
|
+
const bandScale = getCategoricalScale({
|
|
71
|
+
domain: { min: 0, max: 2 },
|
|
72
|
+
range: { min: 0, max: 300 },
|
|
73
|
+
padding: 0,
|
|
74
|
+
});
|
|
75
|
+
const data = [10, 50, 90];
|
|
76
|
+
|
|
77
|
+
const points = toScaledPoints(data, bandScale, yScale);
|
|
78
|
+
|
|
79
|
+
expect(points).toHaveLength(3);
|
|
80
|
+
const bandwidth = bandScale.bandwidth();
|
|
81
|
+
expect(points![0][0]).toBe((bandScale(0) ?? 0) + bandwidth / 2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns null when fewer than 2 valid points remain', () => {
|
|
85
|
+
const data: (number | null)[] = [10, null, null];
|
|
86
|
+
const xData = ['A', 'B', 'C'];
|
|
87
|
+
|
|
88
|
+
expect(toScaledPoints(data, xScale, yScale, xData)).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -14,6 +14,10 @@ type Point = [x: number, y: number];
|
|
|
14
14
|
*
|
|
15
15
|
* When `xData` contains numeric values, those values are fed into the scale
|
|
16
16
|
* instead of the array index so the points honour a numeric X domain.
|
|
17
|
+
*
|
|
18
|
+
* When `xData` is provided, iteration is capped at `xData.length` so a series
|
|
19
|
+
* with more points than the axis has labels does not overflow past the right
|
|
20
|
+
* edge of the chart. The axis is treated as authoritative for the X domain.
|
|
17
21
|
*/
|
|
18
22
|
export const toScaledPoints = (
|
|
19
23
|
data: (number | null)[],
|
|
@@ -22,8 +26,9 @@ export const toScaledPoints = (
|
|
|
22
26
|
xData?: readonly (string | number)[],
|
|
23
27
|
): Point[] | null => {
|
|
24
28
|
const pts: Point[] = [];
|
|
29
|
+
const limit = xData ? Math.min(data.length, xData.length) : data.length;
|
|
25
30
|
|
|
26
|
-
for (let i = 0; i <
|
|
31
|
+
for (let i = 0; i < limit; i++) {
|
|
27
32
|
const value = data[i];
|
|
28
33
|
if (value === null) continue;
|
|
29
34
|
|
|
@@ -76,6 +76,42 @@ export const WithXAxis: Story = {
|
|
|
76
76
|
},
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
+
export const XAxisExplicitNumericData: Story = {
|
|
80
|
+
parameters: {
|
|
81
|
+
layout: 'centered',
|
|
82
|
+
backgrounds: { default: 'light' },
|
|
83
|
+
},
|
|
84
|
+
args: {
|
|
85
|
+
series: sampleSeries,
|
|
86
|
+
width: 400,
|
|
87
|
+
height: 250,
|
|
88
|
+
showXAxis: true,
|
|
89
|
+
xAxis: {
|
|
90
|
+
showLine: true,
|
|
91
|
+
showGrid: true,
|
|
92
|
+
data: [0, 2, 4],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const XAxisStringData: Story = {
|
|
98
|
+
parameters: {
|
|
99
|
+
layout: 'centered',
|
|
100
|
+
backgrounds: { default: 'light' },
|
|
101
|
+
},
|
|
102
|
+
args: {
|
|
103
|
+
series: sampleSeries,
|
|
104
|
+
width: 400,
|
|
105
|
+
height: 250,
|
|
106
|
+
showXAxis: true,
|
|
107
|
+
xAxis: {
|
|
108
|
+
showLine: true,
|
|
109
|
+
showGrid: true,
|
|
110
|
+
data: ['Jan', 'Feb', 'Mar'],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
79
115
|
export const WithStringLabels: Story = {
|
|
80
116
|
parameters: {
|
|
81
117
|
layout: 'centered',
|