@circutor/charts-react 0.2.0 → 0.3.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/README.md +99 -45
- package/dist/types/index.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,19 +15,21 @@ Each component imports only its own Web Component, so your bundler only includes
|
|
|
15
15
|
|
|
16
16
|
### ITIC chart
|
|
17
17
|
|
|
18
|
+
The chart only takes data, plus the language and time zone. It decides what is shown and how: the tooltip, the translations, the axis names, the accessible name, and each phase's name, color and order.
|
|
19
|
+
|
|
18
20
|
```tsx
|
|
19
21
|
import { IticChart } from '@circutor/charts-react';
|
|
20
22
|
|
|
21
23
|
const groups = [
|
|
22
24
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
points: [{ duration: 230, voltage: 93.46, ts: '2024-06-26T20:23:45.220000+02:00' }],
|
|
25
|
+
phase: 'AN,BN,CN',
|
|
26
|
+
assetName: 'Cuadro general',
|
|
27
|
+
points: [{ duration: 230, voltage: 93.46, ts: '2024-06-26T20:23:45.220000+02:00', eventType: 'VOLTAGE_DIP' }],
|
|
26
28
|
},
|
|
27
29
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
points: [{ duration: 20, voltage: 0.4, ts: '2025-01-26T23:04:34.840000+01:00', clickable: false }],
|
|
30
|
+
phase: 'BN',
|
|
31
|
+
assetName: 'Cuadro general',
|
|
32
|
+
points: [{ duration: 20, voltage: 0.4, ts: '2025-01-26T23:04:34.840000+01:00', eventType: 'POWER_INTERRUPT', clickable: false }],
|
|
31
33
|
},
|
|
32
34
|
];
|
|
33
35
|
|
|
@@ -35,7 +37,8 @@ export function PowerQuality() {
|
|
|
35
37
|
return (
|
|
36
38
|
<IticChart
|
|
37
39
|
groups={groups}
|
|
38
|
-
|
|
40
|
+
locale="es"
|
|
41
|
+
timeZone="Europe/Madrid"
|
|
39
42
|
onPointClick={({ detail }) => console.log(detail.point, detail.group)}
|
|
40
43
|
/>
|
|
41
44
|
);
|
|
@@ -44,66 +47,115 @@ export function PowerQuality() {
|
|
|
44
47
|
|
|
45
48
|
| Prop | Type | Default | Description |
|
|
46
49
|
|---|---|---|---|
|
|
47
|
-
| `groups` | `ITICChartMarks[]` | – | Point groups
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `tooltipFormatter` | `(point, group) => string` | English tooltip | HTML of a point's tooltip. |
|
|
50
|
+
| `groups` | `ITICChartMarks[]` | – | Point groups (see [Data](#data)). |
|
|
51
|
+
| `locale` | `string` | `'en'` | Language of the texts, event types, axes, `aria-label`, translated phases, dates and numbers. Only the language is used (`es-ES` → `es`); anything other than `ca`, `de`, `en`, `es`, `fr` or `pt` falls back to `en`. |
|
|
52
|
+
| `timeZone` | `string` (IANA) | The browser's | Time zone of the dates in the tooltip, e.g. `Europe/Madrid`. An invalid value uses the browser's and logs a `console.error`. |
|
|
51
53
|
| `onPointClick` | `(event: CustomEvent<ITICPointClickDetail>) => void` | – | A clickable point was clicked. `event.detail` holds the `point` and `group` objects you passed in `groups`. |
|
|
52
|
-
| `aria-label` | `string` |
|
|
54
|
+
| `aria-label` | `string` | Translated "ITIC chart" | Accessible name. The element has `role="img"`, since the chart is a canvas. The default follows `locale`; a value you set is left alone. |
|
|
53
55
|
|
|
54
|
-
The `ITICChartMarks`, `ITICPoint
|
|
56
|
+
The `ITICChartMarks`, `ITICPoint` and `ITICPointClickDetail` types are exported by this package.
|
|
55
57
|
|
|
56
58
|
The background is transparent; set it with the `--itic-chart-background` CSS custom property, e.g. `itic-chart { --itic-chart-background: #fafafb; }`.
|
|
57
59
|
|
|
60
|
+
#### Data
|
|
61
|
+
|
|
62
|
+
| Group field | Type | Description |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `phase` | `string` | Phase code (`AN`, `AB,BC,CA`, `ALL`, `NONE`…). Sets the group's name, color and order (see [Phases](#phases)). |
|
|
65
|
+
| `assetName` | `string`, optional | Asset name, shown in the tooltip. |
|
|
66
|
+
| `points` | `ITICPoint[]` | The voltage events. |
|
|
67
|
+
|
|
68
|
+
| Point field | Type | Description |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `duration` | `number` | Duration, in ms. |
|
|
71
|
+
| `voltage` | `number` | Voltage, in % of the nominal voltage. |
|
|
72
|
+
| `ts` | `string` | Date of the event, ISO 8601. |
|
|
73
|
+
| `clickable` | `boolean`, optional | `false` hides the pointer cursor and emits no `onPointClick`. Defaults to `true`. |
|
|
74
|
+
| `eventType` | `string`, optional | Event type code from the backend (see [Event types](#event-types)), translated in the tooltip. |
|
|
75
|
+
| `status` | `string`, optional | Status code from the backend, shown as is. |
|
|
76
|
+
| `min` / `max` | `number`, optional | Minimum and maximum voltage, in V. |
|
|
77
|
+
|
|
78
|
+
The axes have fixed ranges: duration from 1 to 100,000 ms (logarithmic) and voltage from 0 to 200%. **Points outside them aren't drawn** (e.g. a duration below 1 ms or a voltage above 200%).
|
|
79
|
+
|
|
80
|
+
#### Tooltip
|
|
81
|
+
|
|
82
|
+
The tooltip shows these rows, in this order, in the chart's `locale`. Rows without a value are left out, and every value is escaped, so data can't inject HTML.
|
|
83
|
+
|
|
84
|
+
| Row (`en`) | Value | Left out when |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| Asset | `assetName` | missing or empty |
|
|
87
|
+
| Date | `ts` with milliseconds, in `timeZone` (e.g. `Jun 26, 20:23:45.220`, `26 jun, 20:23:45,220`) | never; an invalid date is shown as is |
|
|
88
|
+
| Event | translated `eventType`; unknown codes as is | missing or empty |
|
|
89
|
+
| Duration | e.g. `1,500 ms` (`1500 ms` in `es`) | never |
|
|
90
|
+
| Voltage | rounded to 1 decimal, e.g. `93.5%` (`93,5 %` in `es`) | never |
|
|
91
|
+
| Status | `status` as is | missing or empty |
|
|
92
|
+
| Phase | phase name; `-` for `NONE` | never |
|
|
93
|
+
| Min / Max | 2 decimals, e.g. `207.12 V` | missing or `0` |
|
|
94
|
+
|
|
95
|
+
Numbers use the separators of each language, also in the axis labels: in `es` the duration axis reads `1000`, `10.000`, `100.000` (Spanish doesn't group 4-digit numbers).
|
|
96
|
+
|
|
97
|
+
The tooltip is added to `document.body`, not inside the chart, so containers with `overflow: hidden` don't clip it.
|
|
98
|
+
|
|
99
|
+
#### Phases
|
|
100
|
+
|
|
101
|
+
| Code | Name | Color |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `AN`, `AB` | L1 | `#a67c52` |
|
|
104
|
+
| `BN`, `BC` | L2 | `#20262C` |
|
|
105
|
+
| `CN`, `CA` | L3 | `#B9BDC0` |
|
|
106
|
+
| `AN,BN,CN`, `AB,BC,CA` | L1,L2,L3 | `#00B388` |
|
|
107
|
+
| `ALL` | Translated: All, Todas, Totes, Alle, Toutes | `#4C5CC5` |
|
|
108
|
+
| `A`, `B`, `C` | L1, L2, L3 | ECharts palette |
|
|
109
|
+
| `AN,BN`, `AN,CN`, `BN,CN`, `AB,BC`, `AB,CA`, `BC,CA` | L1,L2 · L1,L3 · L2,L3 | ECharts palette |
|
|
110
|
+
| `NONE` | No name | ECharts palette |
|
|
111
|
+
| Any other | The code as is | ECharts palette |
|
|
112
|
+
|
|
113
|
+
- Groups are sorted by name (`L1`, `L1,L2,L3`, `L2`, `L3`), then by code, whatever order they come in, with `NONE` and then `ALL` last. Groups with the same code keep their order.
|
|
114
|
+
- Phase-neutral and phase-phase codes share names and colors: `AN` and `AB` in the same chart are two "L1" groups you can't tell apart, and one legend item toggles both.
|
|
115
|
+
- `NONE` groups have no name, so they don't appear in the legend (their points are drawn) and their tooltip shows `-` as phase.
|
|
116
|
+
- Phases without a fixed color take the next one of the ECharts palette, so the same code may get different colors in different charts.
|
|
117
|
+
|
|
118
|
+
#### Event types
|
|
119
|
+
|
|
120
|
+
`eventType` codes with a translation (in `ca`, `de`, `en`, `es`, `fr` and `pt`). Any other code is shown as is.
|
|
121
|
+
|
|
122
|
+
| Code | `en` | `es` |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `VOLTAGE_DIP` | Voltage dip | Hueco de tensión |
|
|
125
|
+
| `VOLTAGE_SWELL` | Voltage swell | Aumento de tensión |
|
|
126
|
+
| `POWER_INTERRUPT` | Power interruption | Interrupción |
|
|
127
|
+
| `VOLTAGE_TRANSIENT` | Voltage transient | Transitorio de voltaje |
|
|
128
|
+
| `VOLTAGE_VARIATION` | Voltage variation | Variación de voltaje |
|
|
129
|
+
| `OVER_VOLTAGE` | Overvoltage | Sobretensión |
|
|
130
|
+
| `SUB_VOLTAGE` | Subvoltage | Subtensión |
|
|
131
|
+
|
|
58
132
|
#### Clickable points
|
|
59
133
|
|
|
60
134
|
Every point is clickable by default: it shows a pointer cursor and emits `onPointClick`. Set `clickable: false` on a point to turn both off, e.g. for events without waveforms.
|
|
61
135
|
|
|
62
136
|
#### Your own data in points and groups
|
|
63
137
|
|
|
64
|
-
Points and groups accept any extra field (e.g. `
|
|
138
|
+
Points and groups accept any extra field (e.g. `waveforms`). The chart doesn't read or validate them; it passes them back in `onPointClick`. Cast them to your own types there:
|
|
65
139
|
|
|
66
140
|
```tsx
|
|
67
141
|
import { IticChart, type ITICChartMarks, type ITICPoint } from '@circutor/charts-react';
|
|
68
142
|
|
|
69
143
|
// Use `extends` (or `&`) so your types are assignable to the chart's
|
|
70
144
|
interface EventPoint extends ITICPoint {
|
|
71
|
-
eventType: string;
|
|
72
145
|
waveforms: Waveform[];
|
|
73
146
|
}
|
|
74
147
|
interface AssetGroup extends ITICChartMarks {
|
|
75
|
-
assetName: string;
|
|
76
148
|
points: EventPoint[];
|
|
77
149
|
}
|
|
78
150
|
|
|
79
151
|
<IticChart
|
|
80
152
|
groups={groups}
|
|
153
|
+
locale={locale}
|
|
154
|
+
timeZone={timeZone}
|
|
81
155
|
onPointClick={({ detail }) => openWaveforms(detail.point as EventPoint, detail.group as AssetGroup)}
|
|
82
156
|
/>;
|
|
83
157
|
```
|
|
84
158
|
|
|
85
|
-
#### Translations and custom tooltips
|
|
86
|
-
|
|
87
|
-
The axis names and the default tooltip are in English. Pass `xAxisName`, `yAxisName` and a `tooltipFormatter` to translate them or to show other fields, formatting dates with your user's locale and time zone:
|
|
88
|
-
|
|
89
|
-
```tsx
|
|
90
|
-
const formatDate = new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'medium', timeZone }).format;
|
|
91
|
-
|
|
92
|
-
// Define it outside the component (or with useCallback): a new function redraws the chart
|
|
93
|
-
const tooltipFormatter: ITICTooltipFormatter = (point, group) =>
|
|
94
|
-
[
|
|
95
|
-
`<strong>${t('asset')}</strong>: ${escapeHtml((group as AssetGroup).assetName)}`,
|
|
96
|
-
`<strong>${t('timestamp')}</strong>: ${formatDate(new Date(point.ts))}`,
|
|
97
|
-
`<strong>${t('duration')}</strong>: ${point.duration} ms`,
|
|
98
|
-
].join('<br/>');
|
|
99
|
-
|
|
100
|
-
<IticChart groups={groups} xAxisName={t('duration')} yAxisName={t('voltage')} tooltipFormatter={tooltipFormatter} />;
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
> **The HTML returned by `tooltipFormatter` is inserted as is.** Escape any value that may contain HTML (names, free text) yourself. The default tooltip escapes every value.
|
|
104
|
-
|
|
105
|
-
The tooltip is added to `document.body`, not inside the chart, so containers with `overflow: hidden` don't clip it.
|
|
106
|
-
|
|
107
159
|
### Size and data updates
|
|
108
160
|
|
|
109
161
|
- The chart takes the full width of its container and is `400px` high by default. Any CSS rule on `itic-chart` wins over that default, e.g. `itic-chart { height: 300px; }`, or, to grow with a flex container:
|
|
@@ -112,11 +164,11 @@ The tooltip is added to `document.body`, not inside the chart, so containers wit
|
|
|
112
164
|
.card itic-chart { flex: 1; min-height: 400px; }
|
|
113
165
|
```
|
|
114
166
|
The chart redraws automatically whenever its size changes.
|
|
115
|
-
- Passing a new `groups` array redraws the chart; groups that are no longer present are removed.
|
|
167
|
+
- Passing a new `groups` array, `locale` or `timeZone` redraws the chart; groups that are no longer present are removed.
|
|
116
168
|
|
|
117
169
|
### Next.js (App Router)
|
|
118
170
|
|
|
119
|
-
- The wrappers are client components (`'use client'`). A Server Component can render `<IticChart>` with serializable props (`groups`,
|
|
171
|
+
- The wrappers are client components (`'use client'`). A Server Component can render `<IticChart>` with serializable props (`groups`, `locale`, `timeZone`, `aria-label`), but `onPointClick` is a function: pass it from your own `'use client'` component.
|
|
120
172
|
- Server-side rendering works without `next/dynamic` or `ssr: false`: importing the wrappers doesn't touch the DOM, the server renders an empty `<itic-chart>` and the chart draws in the browser after hydration, without hydration warnings.
|
|
121
173
|
|
|
122
174
|
### Testing with Vitest and jsdom
|
|
@@ -129,9 +181,11 @@ import { vi } from 'vitest';
|
|
|
129
181
|
import type { ITICChartMarks, ITICPointClickDetail } from '@circutor/charts-react';
|
|
130
182
|
|
|
131
183
|
vi.mock('@circutor/charts-react', () => ({
|
|
132
|
-
// One button per point, so tests can find points and click them
|
|
133
|
-
IticChart: ({ groups = [], onPointClick, 'aria-label': ariaLabel }: {
|
|
184
|
+
// One button per point, so tests can find points and click them. `locale` and `timeZone` are ignored.
|
|
185
|
+
IticChart: ({ groups = [], onPointClick, 'aria-label': ariaLabel = 'ITIC chart' }: {
|
|
134
186
|
groups?: ITICChartMarks[];
|
|
187
|
+
locale?: string;
|
|
188
|
+
timeZone?: string;
|
|
135
189
|
onPointClick?: (event: CustomEvent<ITICPointClickDetail>) => void;
|
|
136
190
|
'aria-label'?: string;
|
|
137
191
|
}) => (
|
|
@@ -139,11 +193,11 @@ vi.mock('@circutor/charts-react', () => ({
|
|
|
139
193
|
{groups.flatMap(group =>
|
|
140
194
|
group.points.map((point, index) => (
|
|
141
195
|
<button
|
|
142
|
-
key={`${group.
|
|
196
|
+
key={`${group.phase}-${index}`}
|
|
143
197
|
disabled={point.clickable === false}
|
|
144
198
|
onClick={() => onPointClick?.(new CustomEvent('pointClick', { detail: { point, group } }))}
|
|
145
199
|
>
|
|
146
|
-
{group.
|
|
200
|
+
{group.phase} {point.ts}
|
|
147
201
|
</button>
|
|
148
202
|
)),
|
|
149
203
|
)}
|
|
@@ -152,4 +206,4 @@ vi.mock('@circutor/charts-react', () => ({
|
|
|
152
206
|
}));
|
|
153
207
|
```
|
|
154
208
|
|
|
155
|
-
Then `fireEvent.click(screen.getByText('
|
|
209
|
+
Then `fireEvent.click(screen.getByText('AN 2024-06-26T20:23:45.220000+02:00'))` calls your `onPointClick` with the original point.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './components/stencil-generated/components';
|
|
2
|
-
export type { ITICChartMarks, ITICPoint, ITICPointClickDetail
|
|
2
|
+
export type { ITICChartMarks, ITICPoint, ITICPointClickDetail } from '@circutor/charts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@circutor/charts-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "React wrappers for the @circutor/charts Web Components",
|
|
5
5
|
"author": "apinto <apinto@circutor.com>",
|
|
6
6
|
"license": "ISC",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"typescript": "^5.7.3"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@circutor/charts": "^0.
|
|
27
|
+
"@circutor/charts": "^0.3.1",
|
|
28
28
|
"@stencil/react-output-target": "^1.6.5"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "cb1dba202f82c6b0493e8d262426ff6b2b70d3d5"
|
|
38
38
|
}
|