zz-meridian 0.20.0 → 0.22.0
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/package.json +1 -1
- package/payload/CHANGELOG.md +16 -0
- package/payload/package.json +1 -1
- package/payload/src/components/charts/trend-chart/README.md +2 -2
- package/payload/src/components/charts/trend-chart/index.tsx +4 -1
- package/payload/src/components/patterns/metric-tile/README.md +2 -2
- package/payload/src/components/patterns/metric-tile/index.tsx +6 -3
- package/payload/src/components/ui/delta/README.md +3 -3
- package/payload/src/components/ui/delta/index.tsx +18 -4
- package/payload/src/components/ui/delta/preview.tsx +4 -0
- package/payload/tests/metric-tile-delta-format.test.tsx +25 -0
- package/payload/tests/trend-chart-whole.test.tsx +28 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zz-meridian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Bring ZZ Meridian, a dashboard design system, into a Next.js project, or start a new dashboard on it. Copies the files in; nothing depends on this package at runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/payload/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Every release of ZZ Meridian, newest first. Versions follow semver: a removed or renamed token, prop or card is major; a new card, token or variant is minor; a corrected value is a patch. Each entry says what breaks and what to do instead.
|
|
4
4
|
|
|
5
|
+
## [0.22.0] · 2026-10-20
|
|
6
|
+
|
|
7
|
+
### New
|
|
8
|
+
|
|
9
|
+
- **A tile whose figure is a share reads its change in points** ([#74](https://github.com/zhixuan312/zz-meridian/issues/74)). `MetricTile` takes `deltaFormat` and hands it to `Delta`, whose `format` now takes a name as well as a function: `percent` (the default, a share of the earlier value) or `points`, so a share that went from 33.0% to 46.4% reads "▲13.4 pts" (`delta={0.134}`) with the same arrow, colour and screen-reader word, where it could only read "▲40.6%". `DeltaFormat` and `DELTA_FORMATS` are exported from `@/components/ui/delta`.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **A Delta with no change reads its format** (#74): "0.0 pts" under `points`, or what a function makes of zero, where it printed a literal "0%" whatever the format. With no format given it still reads "0%".
|
|
14
|
+
|
|
15
|
+
## [0.21.0] · 2026-10-20
|
|
16
|
+
|
|
17
|
+
### New
|
|
18
|
+
|
|
19
|
+
- **`TrendChart` takes `whole`** ([#73](https://github.com/zhixuan312/zz-meridian/issues/73)), as `ColumnChart` does since 0.20.0: the values are counts, a stack of counts too, and every level is a whole number, so a running total whose top is 10 draws no level at 2.5 or 7.5. Pass it on a chart of counts; a chart of a quantity that is not one keeps its halves.
|
|
20
|
+
|
|
5
21
|
## [0.20.0] · 2026-10-20
|
|
6
22
|
|
|
7
23
|
### New
|
package/payload/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zz-meridian-template",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "ZZ Meridian: a dashboard design system and starter. Six layers, from DTCG tokens through four layers of React components to product workspaces, and a Design Atlas, in a light and a dark theme.",
|
|
@@ -7,7 +7,7 @@ Status: beta
|
|
|
7
7
|
## Anatomy
|
|
8
8
|
|
|
9
9
|
1. **Plot**: horizontal gridlines in `chart-grid`, the baseline in `chart-axis`, 1px, crisp.
|
|
10
|
-
2. **Y axis**: 3 or 4 "nice" ticks from zero, labels `text-2xs` `ink-3`, tabular, right-aligned in a gutter sized to the longest label. Axis labels drop cents and exact digits (`$12`, `1.2M`), and money from $10,000 reads at a glance (`$25K`, `$5.0B`) so a large scale does not widen the gutter; the tooltip and table carry the exact amounts.
|
|
10
|
+
2. **Y axis**: 3 or 4 "nice" ticks from zero (whole numbers only with `whole`, for a chart of counts or a stack of them, so a top of 10 draws no level at 2.5 or 7.5), labels `text-2xs` `ink-3`, tabular, right-aligned in a gutter sized to the longest label. Axis labels drop cents and exact digits (`$12`, `1.2M`), and money from $10,000 reads at a glance (`$25K`, `$5.0B`) so a large scale does not widen the gutter; the tooltip and table carry the exact amounts.
|
|
11
11
|
3. **X axis**: dates, `text-2xs` `ink-3`, one label per 92px, counted back from today so the last day is always labelled.
|
|
12
12
|
4. **Series**: monotone curves that never overshoot their data.
|
|
13
13
|
5. **Band** (with `band`): the region from the floor up to the band's ceiling, in `fill-track`, under the gridlines, its top edge drawn as the axis is, and its label at an end of that edge — in `ink-2`, since it stands over the fill as often as over the card and `ink-3` on `fill-track` is 4.43:1 in light.
|
|
@@ -125,7 +125,7 @@ import { TrendChart } from '@/components/charts/trend-chart';
|
|
|
125
125
|
/>
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
Props: `dates`, `series` (`key`, `label`, `values`, `color`, `kind`), `format` (`count` | `compact` | `cost` | `cost-compact` | `duration` | `percent`), `height` (number or `fill`), `label`, `legend` (defaults to two or more series), `tick`, `stacked`, `baseline`, `band`, `floor`, `marks`, `running` (the last point is still in progress).
|
|
128
|
+
Props: `dates`, `series` (`key`, `label`, `values`, `color`, `kind`), `format` (`count` | `compact` | `cost` | `cost-compact` | `duration` | `percent`), `height` (number or `fill`), `label`, `legend` (defaults to two or more series), `tick`, `stacked`, `whole` (the values are counts: whole levels only), `baseline`, `band`, `floor`, `marks`, `running` (the last point is still in progress).
|
|
129
129
|
|
|
130
130
|
The colour of a stacked part is `stackPart(place, parts)` from `@/components/charts/scale`, the one mix every one-hue stack takes: the accent into the surface, by the part's place from the bottom.
|
|
131
131
|
|
|
@@ -97,6 +97,7 @@ export function TrendChart({
|
|
|
97
97
|
legend,
|
|
98
98
|
tick,
|
|
99
99
|
stacked = false,
|
|
100
|
+
whole = false,
|
|
100
101
|
baseline,
|
|
101
102
|
band,
|
|
102
103
|
floor,
|
|
@@ -118,6 +119,8 @@ export function TrendChart({
|
|
|
118
119
|
tick?: (date: string) => string;
|
|
119
120
|
/** The series are parts of one whole: bands stacked in order, the first at the bottom, each in its categorical slot. */
|
|
120
121
|
stacked?: boolean;
|
|
122
|
+
/** The values are counts, a stack of counts too: every level is a whole number, so a top of 10 draws none at 2.5 or 7.5 (#73). */
|
|
123
|
+
whole?: boolean;
|
|
121
124
|
/** What is usual, drawn as a quiet dashed line across the plot with its label at the end: `{ value: 296, label: 'Median 296ms' }`. */
|
|
122
125
|
baseline?: { value: number; label: string };
|
|
123
126
|
/**
|
|
@@ -177,7 +180,7 @@ export function TrendChart({
|
|
|
177
180
|
const from = floor === undefined ? 0 : Math.min(floor, ...values);
|
|
178
181
|
// The levels, and the floor the plot is drawn to: `ticks[0]` is 0 unless the chart was given a floor, and the two ends
|
|
179
182
|
// of the scale are what every point, area, band and level line is measured against.
|
|
180
|
-
const ticks = ticksBetween(from, max, h < 200 ? 3 : 4);
|
|
183
|
+
const ticks = ticksBetween(from, max, h < 200 ? 3 : 4, whole);
|
|
181
184
|
const low = ticks[0];
|
|
182
185
|
const top = ticks[ticks.length - 1];
|
|
183
186
|
const left = Math.max(...ticks.map((t) => axis(t).length)) * 6.4 + 12;
|
|
@@ -20,7 +20,7 @@ Card, Delta, Status dot, Tooltip, Sparkline. Tiles sit in a `Row split="tiles"`,
|
|
|
20
20
|
|
|
21
21
|
## Data
|
|
22
22
|
|
|
23
|
-
- `value` is the period's figure; `points` one value a point of the same period, oldest first (days on a page of days), or `null` at a point nothing was measured for, which the Sparkline draws as a gap rather than a fall to zero; `delta` the change as a fraction (0.057 is +5.7%), or `null` when there is no earlier period. `compare` names what the change is measured against (default "vs previous period"; "vs the half hour before" when the window is not the period). `note` is a short line in that place when no comparison fits, such as who is past due, carrying `data-note`; in a narrow tile it takes a second line before it is cut ("296 active · 14 trials · 10 past due" in a third of a page with the assistant's panel open), and a note that is text keeps the whole of itself in its title when the tile cuts it. On a compact tile it stays on the tile's one line.
|
|
23
|
+
- `value` is the period's figure; `points` one value a point of the same period, oldest first (days on a page of days), or `null` at a point nothing was measured for, which the Sparkline draws as a gap rather than a fall to zero; `delta` the change as a fraction (0.057 is +5.7%), or `null` when there is no earlier period; `deltaFormat="points"` reads it in percentage points for a figure that is itself a share (a share from 33.0% to 46.4% is `delta={0.134}`, "13.4 pts"). `compare` names what the change is measured against (default "vs previous period"; "vs the half hour before" when the window is not the period). `note` is a short line in that place when no comparison fits, such as who is past due, carrying `data-note`; in a narrow tile it takes a second line before it is cut ("296 active · 14 trials · 10 past due" in a third of a page with the assistant's panel open), and a note that is text keeps the whole of itself in its title when the tile cuts it. On a compact tile it stays on the tile's one line.
|
|
24
24
|
- `intent` says which way is good: `up` (requests), `down` (errors, latency), `neutral` (compute cost).
|
|
25
25
|
- `value` may be a word for a categorical state ("Likely new", "On track"); it is set at `text-2xl` instead of the figure size and wraps to a second line rather than losing its end ("03 Oct 2026" in a narrow tile), and `format` is not used.
|
|
26
26
|
- `format` turns the number into its text: a name (`count`, `compact`, `cost`, `cost-compact`, `duration`, `percent`), which a server page can pass, or a function from a client component; the default split recognises "$298.43" (any currency symbol from `app.currency`), "2.9M", "0.90%" and "294ms".
|
|
@@ -139,4 +139,4 @@ import { MetricTile } from '@/components/patterns/metric-tile';
|
|
|
139
139
|
<MetricTile size="compact" label="Northwind Labs" value={41_200} format="cost-compact" delta={0.12} compare="vs last week" note="Scale" points={last14Days} />
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
Props: `label`, `value`, `format`, `split`, `delta`, `compare`, `intent`, `note`, `points`, `baseline`, `finding`, `hint`, `icon`, `emphasis`, `size`, `className`. A compact tile holds a container, so it takes its cell's width and never sizes itself from what it holds: give it a grid cell or a width.
|
|
142
|
+
Props: `label`, `value`, `format`, `split`, `delta`, `deltaFormat`, `compare`, `intent`, `note`, `points`, `baseline`, `finding`, `hint`, `icon`, `emphasis`, `size`, `className`. A compact tile holds a container, so it takes its cell's width and never sizes itself from what it holds: give it a grid cell or a width.
|
|
@@ -5,7 +5,7 @@ import { cn } from '@/lib/cn';
|
|
|
5
5
|
import { formatDate } from '@/lib/format-date';
|
|
6
6
|
import { fitFigure, formatBy, splitFigure, type NumberFormat } from '@/lib/format';
|
|
7
7
|
import { Card } from '@/components/ui/card';
|
|
8
|
-
import { Delta } from '@/components/ui/delta';
|
|
8
|
+
import { Delta, type DeltaFormat } from '@/components/ui/delta';
|
|
9
9
|
import { Tooltip } from '@/components/ui/tooltip';
|
|
10
10
|
import { Sparkline } from '@/components/charts/sparkline';
|
|
11
11
|
import { useMeridianIndex, usePointMeridian } from '@/components/charts/meridian';
|
|
@@ -47,6 +47,7 @@ export function MetricTile({
|
|
|
47
47
|
hint,
|
|
48
48
|
value,
|
|
49
49
|
delta,
|
|
50
|
+
deltaFormat,
|
|
50
51
|
note,
|
|
51
52
|
compare = 'vs previous period',
|
|
52
53
|
intent = 'up',
|
|
@@ -67,6 +68,8 @@ export function MetricTile({
|
|
|
67
68
|
value: number | string;
|
|
68
69
|
/** Change against the previous period, as a fraction. */
|
|
69
70
|
delta?: number | null;
|
|
71
|
+
/** How the change reads: `percent` (of the earlier value, the default) or `points`, for a figure that is itself a share. */
|
|
72
|
+
deltaFormat?: DeltaFormat | ((n: number) => string);
|
|
70
73
|
/**
|
|
71
74
|
* A short line where the change would sit, for a figure no period comparison fits: who is past due, what is next.
|
|
72
75
|
* On a compact tile it follows the figure on the tile's one line: "$41.2K · next plan".
|
|
@@ -126,7 +129,7 @@ export function MetricTile({
|
|
|
126
129
|
// The figure alone is drawn. Down a wall the words it is against would stand once a tile, so the card that
|
|
127
130
|
// holds the wall says them; here they follow the figure for a screen reader, which meets a tile on its own.
|
|
128
131
|
<p className={cn('flex h-5 items-center justify-self-start', COMPACT_CHANGE)}>
|
|
129
|
-
<Delta value={delta} intent={intent} />
|
|
132
|
+
<Delta value={delta} intent={intent} format={deltaFormat} />
|
|
130
133
|
<span className="sr-only">{' '}{delta === null ? 'No earlier period to compare' : compare}</span>
|
|
131
134
|
</p>
|
|
132
135
|
) : null}
|
|
@@ -185,7 +188,7 @@ export function MetricTile({
|
|
|
185
188
|
<span className="truncate text-ink-3" title="No earlier period to compare">No earlier period to compare</span>
|
|
186
189
|
) : delta !== undefined ? (
|
|
187
190
|
<>
|
|
188
|
-
<Delta value={delta} intent={intent} />
|
|
191
|
+
<Delta value={delta} intent={intent} format={deltaFormat} />
|
|
189
192
|
<span className="leading-5 whitespace-nowrap text-ink-3">{compare}</span>
|
|
190
193
|
</>
|
|
191
194
|
) : note ? (
|
|
@@ -18,11 +18,11 @@ Status: beta
|
|
|
18
18
|
| Down | Latency, error rate, cost per request | `critical-ink` | `positive-ink` |
|
|
19
19
|
| Neutral | Spend, volume where neither direction is good | `ink-2` | `ink-2` |
|
|
20
20
|
|
|
21
|
-
A change under 0.05% is flat: no arrow, "0
|
|
21
|
+
A change under 0.05% is flat: no arrow, `ink-2`, and the format reading no change ("0.0 pts" under `points`; "0%" with no format given). A missing comparison (`null`) is an em dash in `ink-3`: "we measured no change" and "there is nothing to compare with" are different facts.
|
|
22
22
|
|
|
23
23
|
## Sizes
|
|
24
24
|
|
|
25
|
-
One size, `text-xs`, so it sits on the caption line under a figure. The format defaults to a
|
|
25
|
+
One size, `text-xs`, so it sits on the caption line under a figure. The format defaults to `percent`, the change as a share of the earlier value with one decimal; `points` reads a change in a value that is itself a share in percentage points ("13.4 pts", where `percent` would print a share that went from 33.0% to 46.4% as up 40.6%); pass a function for absolute changes ("43ms").
|
|
26
26
|
|
|
27
27
|
## States
|
|
28
28
|
|
|
@@ -64,4 +64,4 @@ import { Delta } from '@/components/ui/delta';
|
|
|
64
64
|
<Delta value={23.73} format={(n) => `$${n}`} intent="neutral" />
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
Props: `value` (a fraction, or null), `format
|
|
67
|
+
Props: `value` (a fraction, or null), `format` (`percent` | `points` | a function), `intent` (`up` | `down` | `neutral`), `className`.
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { cn } from '@/lib/cn';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* How a change reads: `percent` is the change as a share of the earlier value (0.12 reads 12.0%); `points` is a change in
|
|
5
|
+
* a value that is itself a share, in percentage points (0.134 reads 13.4 pts), so a share that went from 33.0% to 46.4%
|
|
6
|
+
* never reads as having risen 40.6%. A name, so a server page can pass it to a client tile (#74).
|
|
7
|
+
*/
|
|
8
|
+
export type DeltaFormat = 'percent' | 'points';
|
|
9
|
+
export const DELTA_FORMATS: Record<DeltaFormat, (n: number) => string> = {
|
|
10
|
+
percent: (n) => `${Math.abs(n * 100).toFixed(1)}%`,
|
|
11
|
+
points: (n) => `${Math.abs(n * 100).toFixed(1)} pts`,
|
|
12
|
+
};
|
|
13
|
+
|
|
3
14
|
/**
|
|
4
15
|
* A change against the previous period. The arrow carries the direction; the colour carries the judgement, which is
|
|
5
16
|
* not the same thing: for latency or errors, down is good. `intent` says which way is good.
|
|
6
17
|
*/
|
|
7
18
|
export function Delta({
|
|
8
19
|
value,
|
|
9
|
-
format
|
|
20
|
+
format,
|
|
10
21
|
intent = 'up',
|
|
11
22
|
className,
|
|
12
23
|
}: {
|
|
13
|
-
/** The change as a fraction (0.12 is +12
|
|
24
|
+
/** The change as a fraction (0.12 is +12%, or +12 points under `points`), or null when there is nothing to compare with. */
|
|
14
25
|
value: number | null;
|
|
15
|
-
|
|
26
|
+
/** How the change reads, a name or a function of its magnitude; `percent` by default. No change reads it too ("0.0 pts"). */
|
|
27
|
+
format?: DeltaFormat | ((n: number) => string);
|
|
16
28
|
/** Which direction is good. `neutral` paints both directions in ink. */
|
|
17
29
|
intent?: 'up' | 'down' | 'neutral';
|
|
18
30
|
className?: string;
|
|
19
31
|
}) {
|
|
20
32
|
if (value === null || !Number.isFinite(value)) return <span className={cn('text-xs text-ink-3', className)}>—</span>;
|
|
21
33
|
const flat = Math.abs(value) < 0.0005;
|
|
34
|
+
const read = typeof format === 'function' ? format : DELTA_FORMATS[format ?? 'percent'];
|
|
22
35
|
const up = value > 0;
|
|
23
36
|
const good = intent === 'neutral' || flat ? null : (up && intent === 'up') || (!up && intent === 'down');
|
|
24
37
|
return (
|
|
@@ -33,7 +46,8 @@ export function Delta({
|
|
|
33
46
|
<path d="M5 1.5 8.5 7h-7z" fill="currentColor" />
|
|
34
47
|
</svg>
|
|
35
48
|
<span className="sr-only">{flat ? 'unchanged' : up ? 'up' : 'down'} </span>
|
|
36
|
-
{
|
|
49
|
+
{/* No change is read as the format reads a change, so a points tile says "0.0 pts", never a literal 0%. */}
|
|
50
|
+
{flat ? (format ? read(0) : '0%') : read(value)}
|
|
37
51
|
</span>
|
|
38
52
|
);
|
|
39
53
|
}
|
|
@@ -19,6 +19,10 @@ export default function DeltaPreview() {
|
|
|
19
19
|
<State label="Flat"><Delta value={0} /></State>
|
|
20
20
|
<State label="Nothing to compare"><Delta value={null} /></State>
|
|
21
21
|
</Specimen>
|
|
22
|
+
<Specimen label="In points" note='format="points": a change in a figure that is itself a share, a refusal share from 33.0% to 46.4%, reads 13.4 points, never 40.6% of itself. No change reads in the same format.'>
|
|
23
|
+
<State label="Rise"><Delta value={0.134} format="points" intent="down" /></State>
|
|
24
|
+
<State label="Flat"><Delta value={0} format="points" intent="down" /></State>
|
|
25
|
+
</Specimen>
|
|
22
26
|
<Specimen label="In context">
|
|
23
27
|
<span className="flex items-center gap-2 text-xs"><Delta value={0.057} /><span className="text-ink-3">vs previous period</span></span>
|
|
24
28
|
</Specimen>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { MetricTile } from '@/components/patterns/metric-tile';
|
|
4
|
+
import { Delta } from '@/components/ui/delta';
|
|
5
|
+
|
|
6
|
+
describe('a tile whose figure is a share (#74)', () => {
|
|
7
|
+
it('reads its change in points under deltaFormat="points", with the same arrow, colour and word', () => {
|
|
8
|
+
const { container } = render(<MetricTile label="Share refused" value={0.464} format="percent" delta={0.134} deltaFormat="points" intent="down" />);
|
|
9
|
+
const change = container.querySelector('.t-num.inline-flex')!;
|
|
10
|
+
expect(change).toHaveTextContent(/^up 13\.4 pts$/);
|
|
11
|
+
expect(change).toHaveClass('text-critical-ink');
|
|
12
|
+
});
|
|
13
|
+
it('still reads a change as a percentage of the earlier value without it', () => {
|
|
14
|
+
const { container } = render(<MetricTile label="Requests" value={1200} delta={0.406} />);
|
|
15
|
+
expect(container.querySelector('.t-num.inline-flex')).toHaveTextContent(/^up 40\.6%$/);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('a Delta with no change', () => {
|
|
20
|
+
it('reads it in its format, never a literal 0%', () => {
|
|
21
|
+
expect(render(<Delta value={0} format="points" />).container).toHaveTextContent(/^unchanged 0\.0 pts$/);
|
|
22
|
+
expect(render(<Delta value={0} format={(n) => `${n.toFixed(2)} ms`} />).container).toHaveTextContent(/^unchanged 0\.00 ms$/);
|
|
23
|
+
expect(render(<Delta value={0} />).container).toHaveTextContent(/^unchanged 0%$/);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
vi.mock('@/components/charts/use-size', async () => {
|
|
5
|
+
const { useRef } = await import('react');
|
|
6
|
+
return { useSize: () => [useRef(null), { width: 640, height: 0 }] as const };
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
import { TrendChart } from '@/components/charts/trend-chart';
|
|
10
|
+
|
|
11
|
+
const dates = ['2026-09-07', '2026-09-14', '2026-09-21', '2026-09-28'];
|
|
12
|
+
/** The levels' labels: the axis text beside each gridline. */
|
|
13
|
+
const levels = (c: HTMLElement) => [...c.querySelectorAll('svg g > line + text')].map((t) => t.textContent);
|
|
14
|
+
|
|
15
|
+
describe('a trend chart of counts (#73)', () => {
|
|
16
|
+
it('draws whole levels only, a running total of ten as a line or as a stack', () => {
|
|
17
|
+
const made = render(<TrendChart label="Items in all" whole dates={dates} series={[{ key: 'n', label: 'Items', values: [2, 5, 7, 10], kind: 'line' }]} />);
|
|
18
|
+
expect(levels(made.container).every((t) => /^\d+$/.test(t ?? ''))).toBe(true);
|
|
19
|
+
expect(levels(made.container)).not.toContain('2.5');
|
|
20
|
+
made.unmount();
|
|
21
|
+
const stack = render(<TrendChart label="Items in all, by team" whole stacked dates={dates} series={[{ key: 'a', label: 'Core', values: [1, 3, 4, 6] }, { key: 'b', label: 'Web', values: [1, 2, 3, 4] }]} />);
|
|
22
|
+
expect(levels(stack.container).every((t) => /^\d+$/.test(t ?? ''))).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it('keeps halves without it, for a quantity that is not a count', () => {
|
|
25
|
+
const { container } = render(<TrendChart label="Hours waited" dates={dates} series={[{ key: 'n', label: 'Hours', values: [2, 5, 7, 10], kind: 'line' }]} />);
|
|
26
|
+
expect(levels(container)).toContain('2.5');
|
|
27
|
+
});
|
|
28
|
+
});
|