@chipmobilesdk/rn-chart 0.1.1 → 0.2.1
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/CHANGELOG.md +126 -74
- package/README.md +337 -314
- package/package.json +46 -49
- package/skills/sdk-rn-chart/SKILL.md +1 -2
- package/src/a11y/summary.ts +170 -177
- package/src/contract/datasource.ts +126 -134
- package/src/contract/errors.ts +129 -133
- package/src/contract/format.ts +162 -168
- package/src/contract/interaction.ts +70 -71
- package/src/contract/marker.ts +75 -75
- package/src/contract/props.ts +57 -60
- package/src/contract/state.ts +88 -94
- package/src/contract/theme.ts +169 -139
- package/src/contract/value.ts +121 -125
- package/src/geometry/bar.ts +271 -256
- package/src/geometry/distribution.ts +197 -200
- package/src/geometry/heatmap.ts +189 -195
- package/src/geometry/hitTest.ts +117 -120
- package/src/geometry/layout.ts +162 -121
- package/src/geometry/markers.ts +55 -0
- package/src/geometry/path.ts +193 -199
- package/src/geometry/progress.ts +223 -204
- package/src/geometry/scale.ts +140 -146
- package/src/geometry/ticks.ts +145 -153
- package/src/geometry/timeSeries.ts +168 -158
- package/src/geometry/types.ts +94 -88
- package/src/index.ts +158 -176
- package/src/react/ChartBoundary.tsx +96 -96
- package/src/react/ChartFrame.tsx +583 -620
- package/src/react/ChartLegend.tsx +211 -177
- package/src/react/ChartThemeProvider.tsx +44 -48
- package/src/react/charts.tsx +154 -161
- package/src/react/useChartLayout.ts +124 -124
- package/src/react/useChartSelection.ts +152 -170
- package/src/render/Axis.tsx +120 -122
- package/src/render/Marks.tsx +173 -205
- package/src/validation/budgets.ts +103 -110
- package/src/validation/validateDatasource.ts +174 -174
package/src/index.ts
CHANGED
|
@@ -1,176 +1,158 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @chipmobilesdk/rn-chart — domain-agnostic charting for ChipMobileSdk-family
|
|
3
|
-
* React Native apps.
|
|
4
|
-
*
|
|
5
|
-
* This is the single public entrypoint (LIB-003). Modules under `geometry/`,
|
|
6
|
-
* `render/`, `validation/`, and the internals of `react/` are private; import
|
|
7
|
-
* them only through this file. Keeping them private is what lets the geometry,
|
|
8
|
-
* the layout maths, and the rendering substrate change without a breaking
|
|
9
|
-
* release.
|
|
10
|
-
*
|
|
11
|
-
* The package makes no network request, calls no AI service, reads no
|
|
12
|
-
* database, persists nothing, and logs no data value or series label. It
|
|
13
|
-
* renders numeric data and app-supplied labels — nothing else.
|
|
14
|
-
*
|
|
15
|
-
* There is deliberately **no insight, message, or narrative surface**: apps
|
|
16
|
-
* render their own text around a chart, and a chart imposes no outer margin
|
|
17
|
-
* that would fight that layout.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Value semantics — the foundation of the contract
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
export type {
|
|
24
|
-
KnownValue,
|
|
25
|
-
NotApplicableValue,
|
|
26
|
-
TotalResult,
|
|
27
|
-
UnknownValue,
|
|
28
|
-
ValueKind,
|
|
29
|
-
ValueState,
|
|
30
|
-
WithheldValue,
|
|
31
|
-
} from './contract/value';
|
|
32
|
-
export {
|
|
33
|
-
isKnown,
|
|
34
|
-
isWithheld,
|
|
35
|
-
known,
|
|
36
|
-
notApplicable,
|
|
37
|
-
participatesInTotals,
|
|
38
|
-
sumKnown,
|
|
39
|
-
unknown,
|
|
40
|
-
withheld,
|
|
41
|
-
} from './contract/value';
|
|
42
|
-
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
// Datasource contract
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
export type {
|
|
47
|
-
CategoryPosition,
|
|
48
|
-
ChartDatasource,
|
|
49
|
-
ChartFamily,
|
|
50
|
-
DataPoint,
|
|
51
|
-
InstantPosition,
|
|
52
|
-
MatrixPosition,
|
|
53
|
-
Position,
|
|
54
|
-
PositionKind,
|
|
55
|
-
Series,
|
|
56
|
-
SeriesPattern,
|
|
57
|
-
} from './contract/datasource';
|
|
58
|
-
export {
|
|
59
|
-
FAMILY_POSITION_KINDS,
|
|
60
|
-
familyAcceptsPositionKind,
|
|
61
|
-
resolveFormat,
|
|
62
|
-
resolveUnit,
|
|
63
|
-
} from './contract/datasource';
|
|
64
|
-
|
|
65
|
-
export type { MarkerKind, ProgressReading, ReferenceMarker } from './contract/marker';
|
|
66
|
-
export { findTarget, isUsableTarget, readProgress } from './contract/marker';
|
|
67
|
-
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// Formatting
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
export type {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
export {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
export {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
export {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
export {
|
|
159
|
-
BarChart,
|
|
160
|
-
DistributionChart,
|
|
161
|
-
HeatmapChart,
|
|
162
|
-
ProgressChart,
|
|
163
|
-
TimeSeriesChart,
|
|
164
|
-
} from './react/charts';
|
|
165
|
-
|
|
166
|
-
export { ChartLegend, buildLegendEntries } from './react/ChartLegend';
|
|
167
|
-
export { ChartThemeProvider, useChartTheme } from './react/ChartThemeProvider';
|
|
168
|
-
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
// Accessibility helpers
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
export {
|
|
173
|
-
buildChartSummary,
|
|
174
|
-
buildPointAnnouncement,
|
|
175
|
-
buildTotalAnnouncement,
|
|
176
|
-
} from './a11y/summary';
|
|
1
|
+
/**
|
|
2
|
+
* @chipmobilesdk/rn-chart — domain-agnostic charting for ChipMobileSdk-family
|
|
3
|
+
* React Native apps.
|
|
4
|
+
*
|
|
5
|
+
* This is the single public entrypoint (LIB-003). Modules under `geometry/`,
|
|
6
|
+
* `render/`, `validation/`, and the internals of `react/` are private; import
|
|
7
|
+
* them only through this file. Keeping them private is what lets the geometry,
|
|
8
|
+
* the layout maths, and the rendering substrate change without a breaking
|
|
9
|
+
* release.
|
|
10
|
+
*
|
|
11
|
+
* The package makes no network request, calls no AI service, reads no
|
|
12
|
+
* database, persists nothing, and logs no data value or series label. It
|
|
13
|
+
* renders numeric data and app-supplied labels — nothing else.
|
|
14
|
+
*
|
|
15
|
+
* There is deliberately **no insight, message, or narrative surface**: apps
|
|
16
|
+
* render their own text around a chart, and a chart imposes no outer margin
|
|
17
|
+
* that would fight that layout.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Value semantics — the foundation of the contract
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
export type {
|
|
24
|
+
KnownValue,
|
|
25
|
+
NotApplicableValue,
|
|
26
|
+
TotalResult,
|
|
27
|
+
UnknownValue,
|
|
28
|
+
ValueKind,
|
|
29
|
+
ValueState,
|
|
30
|
+
WithheldValue,
|
|
31
|
+
} from './contract/value';
|
|
32
|
+
export {
|
|
33
|
+
isKnown,
|
|
34
|
+
isWithheld,
|
|
35
|
+
known,
|
|
36
|
+
notApplicable,
|
|
37
|
+
participatesInTotals,
|
|
38
|
+
sumKnown,
|
|
39
|
+
unknown,
|
|
40
|
+
withheld,
|
|
41
|
+
} from './contract/value';
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Datasource contract
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
export type {
|
|
47
|
+
CategoryPosition,
|
|
48
|
+
ChartDatasource,
|
|
49
|
+
ChartFamily,
|
|
50
|
+
DataPoint,
|
|
51
|
+
InstantPosition,
|
|
52
|
+
MatrixPosition,
|
|
53
|
+
Position,
|
|
54
|
+
PositionKind,
|
|
55
|
+
Series,
|
|
56
|
+
SeriesPattern,
|
|
57
|
+
} from './contract/datasource';
|
|
58
|
+
export {
|
|
59
|
+
FAMILY_POSITION_KINDS,
|
|
60
|
+
familyAcceptsPositionKind,
|
|
61
|
+
resolveFormat,
|
|
62
|
+
resolveUnit,
|
|
63
|
+
} from './contract/datasource';
|
|
64
|
+
|
|
65
|
+
export type { MarkerKind, ProgressReading, ReferenceMarker } from './contract/marker';
|
|
66
|
+
export { findTarget, isUsableTarget, readProgress } from './contract/marker';
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Formatting
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
export type { ChartFormatters, FormatContext, FormatHint, SeriesRef } from './contract/format';
|
|
72
|
+
export {
|
|
73
|
+
NON_VALUE_TEXT,
|
|
74
|
+
formatLegendDefault,
|
|
75
|
+
formatPositionDefault,
|
|
76
|
+
formatValueDefault,
|
|
77
|
+
seriesRef,
|
|
78
|
+
} from './contract/format';
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// State and errors
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
export type {
|
|
84
|
+
ChartStateContent,
|
|
85
|
+
ChartStatus,
|
|
86
|
+
DeclarableChartStatus,
|
|
87
|
+
StatusInputs,
|
|
88
|
+
} from './contract/state';
|
|
89
|
+
export {
|
|
90
|
+
STATUS_PRECEDENCE,
|
|
91
|
+
drawsData,
|
|
92
|
+
resolveStatus,
|
|
93
|
+
suppressesInteraction,
|
|
94
|
+
} from './contract/state';
|
|
95
|
+
|
|
96
|
+
export type {
|
|
97
|
+
ChartError,
|
|
98
|
+
ChartErrorCategory,
|
|
99
|
+
ChartErrorCode,
|
|
100
|
+
ChartFailureHandler,
|
|
101
|
+
ChartFailureReport,
|
|
102
|
+
} from './contract/errors';
|
|
103
|
+
export { isChartError } from './contract/errors';
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Interaction
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
export type {
|
|
109
|
+
ChartInteractionEvent,
|
|
110
|
+
InteractionProps,
|
|
111
|
+
PointInteractionEvent,
|
|
112
|
+
SelectionRef,
|
|
113
|
+
SeriesInteractionEvent,
|
|
114
|
+
} from './contract/interaction';
|
|
115
|
+
export { sameSelection } from './contract/interaction';
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Theme
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
export type { ChartTheme, LegendEntry, NonValueTexture, TextStyleTokens } from './contract/theme';
|
|
121
|
+
export { defaultChartTheme, resolveSeriesColor } from './contract/theme';
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Props and accessibility
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
export type { BaseChartProps, ChartAccessibility, ValueFormatter } from './contract/props';
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Dataset budgets
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
export type { BudgetCheck, DatasetBudget } from './validation/budgets';
|
|
132
|
+
export { DATASET_BUDGETS, checkBudget } from './validation/budgets';
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Components
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
export type {
|
|
138
|
+
BarChartProps,
|
|
139
|
+
DistributionChartProps,
|
|
140
|
+
HeatmapChartProps,
|
|
141
|
+
ProgressChartProps,
|
|
142
|
+
TimeSeriesChartProps,
|
|
143
|
+
} from './react/charts';
|
|
144
|
+
export {
|
|
145
|
+
BarChart,
|
|
146
|
+
DistributionChart,
|
|
147
|
+
HeatmapChart,
|
|
148
|
+
ProgressChart,
|
|
149
|
+
TimeSeriesChart,
|
|
150
|
+
} from './react/charts';
|
|
151
|
+
|
|
152
|
+
export { ChartLegend, buildLegendEntries } from './react/ChartLegend';
|
|
153
|
+
export { ChartThemeProvider, useChartTheme } from './react/ChartThemeProvider';
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Accessibility helpers
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
export { buildChartSummary, buildPointAnnouncement, buildTotalAnnouncement } from './a11y/summary';
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-chart render-failure containment (FR-038a, FR-038b).
|
|
3
|
-
*
|
|
4
|
-
* React 19 still has no hook equivalent of an error boundary —
|
|
5
|
-
* `getDerivedStateFromError` and `componentDidCatch` remain class-only — so
|
|
6
|
-
* this is a class. Forty lines beats adding a dependency for it.
|
|
7
|
-
*
|
|
8
|
-
* Three behaviours are deliberate:
|
|
9
|
-
*
|
|
10
|
-
* - **Reset only on a real change.** The boundary clears its error when the
|
|
11
|
-
* datasource identity or chart key changes, never on an unrelated parent
|
|
12
|
-
* re-render. A deterministic geometry defect therefore cannot pin the CPU in
|
|
13
|
-
* a render-fail-render loop.
|
|
14
|
-
* - **Report once per distinct failure.** De-duplicated by signature, so a
|
|
15
|
-
* chart re-rendered by a sibling's state change does not re-report.
|
|
16
|
-
* - **Redact.** The report carries the chart identity and a catalogue message.
|
|
17
|
-
* A third-party exception's own text is never copied in, because we cannot
|
|
18
|
-
* promise it is free of data (POL-011).
|
|
19
|
-
*/
|
|
20
|
-
import React from 'react';
|
|
21
|
-
|
|
22
|
-
import type { ChartFamily } from '../contract/datasource';
|
|
23
|
-
import {
|
|
24
|
-
chartError,
|
|
25
|
-
failureSignature,
|
|
26
|
-
toFailureReport,
|
|
27
|
-
type ChartError,
|
|
28
|
-
type ChartFailureHandler,
|
|
29
|
-
} from '../contract/errors';
|
|
30
|
-
|
|
31
|
-
interface Props {
|
|
32
|
-
readonly family: ChartFamily;
|
|
33
|
-
readonly chartId?: string;
|
|
34
|
-
readonly onFailure?: ChartFailureHandler;
|
|
35
|
-
/** Changing this clears a contained failure. */
|
|
36
|
-
readonly resetKey: unknown;
|
|
37
|
-
readonly renderFallback: (error: ChartError) => React.ReactNode;
|
|
38
|
-
readonly children: React.ReactNode;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface State {
|
|
42
|
-
readonly error?: ChartError;
|
|
43
|
-
readonly resetKey: unknown;
|
|
44
|
-
readonly reportedSignature?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class ChartBoundary extends React.Component<Props, State> {
|
|
48
|
-
constructor(props: Props) {
|
|
49
|
-
super(props);
|
|
50
|
-
this.state = { resetKey: props.resetKey };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static getDerivedStateFromProps(props: Props, state: State): State | null {
|
|
54
|
-
if (props.resetKey !== state.resetKey) {
|
|
55
|
-
// New data: give the chart another chance.
|
|
56
|
-
return { resetKey: props.resetKey, error: undefined };
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
static getDerivedStateFromError(): Partial<State> {
|
|
62
|
-
// This must set the error, not just acknowledge it. Returning an empty
|
|
63
|
-
// object leaves render() showing the children again, they throw again,
|
|
64
|
-
// and React escalates past the boundary — which is exactly the
|
|
65
|
-
// screen-blanking failure this class exists to prevent.
|
|
66
|
-
//
|
|
67
|
-
// componentDidCatch then refines this into a report carrying the chart
|
|
68
|
-
// identity and family.
|
|
69
|
-
return { error: chartError('render', 'renderException') };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
componentDidCatch(error: unknown, info: { componentStack?: string }): void {
|
|
73
|
-
const report = toFailureReport(
|
|
74
|
-
this.props.family,
|
|
75
|
-
error,
|
|
76
|
-
this.props.chartId,
|
|
77
|
-
info?.componentStack ?? undefined,
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const signature = failureSignature(report);
|
|
81
|
-
if (this.state.reportedSignature === signature) {
|
|
82
|
-
this.setState({ error: report.error });
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
this.setState({ error: report.error, reportedSignature: signature });
|
|
87
|
-
this.props.onFailure?.(report);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
render(): React.ReactNode {
|
|
91
|
-
if (this.state.error) {
|
|
92
|
-
return this.props.renderFallback(this.state.error);
|
|
93
|
-
}
|
|
94
|
-
return this.props.children;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Per-chart render-failure containment (FR-038a, FR-038b).
|
|
3
|
+
*
|
|
4
|
+
* React 19 still has no hook equivalent of an error boundary —
|
|
5
|
+
* `getDerivedStateFromError` and `componentDidCatch` remain class-only — so
|
|
6
|
+
* this is a class. Forty lines beats adding a dependency for it.
|
|
7
|
+
*
|
|
8
|
+
* Three behaviours are deliberate:
|
|
9
|
+
*
|
|
10
|
+
* - **Reset only on a real change.** The boundary clears its error when the
|
|
11
|
+
* datasource identity or chart key changes, never on an unrelated parent
|
|
12
|
+
* re-render. A deterministic geometry defect therefore cannot pin the CPU in
|
|
13
|
+
* a render-fail-render loop.
|
|
14
|
+
* - **Report once per distinct failure.** De-duplicated by signature, so a
|
|
15
|
+
* chart re-rendered by a sibling's state change does not re-report.
|
|
16
|
+
* - **Redact.** The report carries the chart identity and a catalogue message.
|
|
17
|
+
* A third-party exception's own text is never copied in, because we cannot
|
|
18
|
+
* promise it is free of data (POL-011).
|
|
19
|
+
*/
|
|
20
|
+
import React from 'react';
|
|
21
|
+
|
|
22
|
+
import type { ChartFamily } from '../contract/datasource';
|
|
23
|
+
import {
|
|
24
|
+
chartError,
|
|
25
|
+
failureSignature,
|
|
26
|
+
toFailureReport,
|
|
27
|
+
type ChartError,
|
|
28
|
+
type ChartFailureHandler,
|
|
29
|
+
} from '../contract/errors';
|
|
30
|
+
|
|
31
|
+
interface Props {
|
|
32
|
+
readonly family: ChartFamily;
|
|
33
|
+
readonly chartId?: string;
|
|
34
|
+
readonly onFailure?: ChartFailureHandler;
|
|
35
|
+
/** Changing this clears a contained failure. */
|
|
36
|
+
readonly resetKey: unknown;
|
|
37
|
+
readonly renderFallback: (error: ChartError) => React.ReactNode;
|
|
38
|
+
readonly children: React.ReactNode;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface State {
|
|
42
|
+
readonly error?: ChartError;
|
|
43
|
+
readonly resetKey: unknown;
|
|
44
|
+
readonly reportedSignature?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class ChartBoundary extends React.Component<Props, State> {
|
|
48
|
+
constructor(props: Props) {
|
|
49
|
+
super(props);
|
|
50
|
+
this.state = { resetKey: props.resetKey };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static getDerivedStateFromProps(props: Props, state: State): State | null {
|
|
54
|
+
if (props.resetKey !== state.resetKey) {
|
|
55
|
+
// New data: give the chart another chance.
|
|
56
|
+
return { resetKey: props.resetKey, error: undefined };
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static getDerivedStateFromError(): Partial<State> {
|
|
62
|
+
// This must set the error, not just acknowledge it. Returning an empty
|
|
63
|
+
// object leaves render() showing the children again, they throw again,
|
|
64
|
+
// and React escalates past the boundary — which is exactly the
|
|
65
|
+
// screen-blanking failure this class exists to prevent.
|
|
66
|
+
//
|
|
67
|
+
// componentDidCatch then refines this into a report carrying the chart
|
|
68
|
+
// identity and family.
|
|
69
|
+
return { error: chartError('render', 'renderException') };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
componentDidCatch(error: unknown, info: { componentStack?: string }): void {
|
|
73
|
+
const report = toFailureReport(
|
|
74
|
+
this.props.family,
|
|
75
|
+
error,
|
|
76
|
+
this.props.chartId,
|
|
77
|
+
info?.componentStack ?? undefined,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const signature = failureSignature(report);
|
|
81
|
+
if (this.state.reportedSignature === signature) {
|
|
82
|
+
this.setState({ error: report.error });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.setState({ error: report.error, reportedSignature: signature });
|
|
87
|
+
this.props.onFailure?.(report);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render(): React.ReactNode {
|
|
91
|
+
if (this.state.error) {
|
|
92
|
+
return this.props.renderFallback(this.state.error);
|
|
93
|
+
}
|
|
94
|
+
return this.props.children;
|
|
95
|
+
}
|
|
96
|
+
}
|