@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 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
- groupName: 'L1,L2,L3',
24
- color: '#00B388',
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
- groupName: 'L2',
29
- color: '#20262C',
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
- aria-label="ITIC curve of the voltage events"
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: `groupName` (legend label), `color` and `points` (`duration` in ms, `voltage` in % of nominal, `ts` timestamp, optional `clickable`). |
48
- | `xAxisName` | `string` | `Duration (ms)` | Name of the duration axis. |
49
- | `yAxisName` | `string` | `Voltage (% of nominal voltage)` | Name of the voltage axis. |
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` | – | Accessible name. The element has `role="img"`, since the chart is a canvas. |
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`, `ITICPointClickDetail` and `ITICTooltipFormatter` types are exported by this package.
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. `eventType`, `waveforms`, `assetName`). The chart doesn't read or validate them; it passes them back in `onPointClick` and `tooltipFormatter`. Cast them to your own types there:
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`, axis names, `aria-label`), but `onPointClick` and `tooltipFormatter` are functions: pass them from your own `'use client'` component.
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.groupName}-${index}`}
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.groupName} {point.ts}
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('L1 2024-06-26T20:23:45.220000+02:00'))` calls your `onPointClick` with the original point.
209
+ Then `fireEvent.click(screen.getByText('AN 2024-06-26T20:23:45.220000+02:00'))` calls your `onPointClick` with the original point.
@@ -1,2 +1,2 @@
1
1
  export * from './components/stencil-generated/components';
2
- export type { ITICChartMarks, ITICPoint, ITICPointClickDetail, ITICTooltipFormatter } from '@circutor/charts';
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.2.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.2.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": "8a3014c8d5a5691aa784640a5ad5008c661e7a57"
37
+ "gitHead": "cb1dba202f82c6b0493e8d262426ff6b2b70d3d5"
38
38
  }