@micha.bigler/ui-core-micha 2.26.4 → 2.28.2

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.
@@ -0,0 +1,203 @@
1
+ # WORK ORDER CHART-4 (ui-core-micha) — TimeSeriesChart renders no chart body (zero height)
2
+
3
+ **EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is not
4
+ addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not shell
5
+ out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked **directly
6
+ via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
7
+ and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
8
+ file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
9
+ **Known at WO authoring time: the Codex workspace is out of credits for this session — the fallback
10
+ applies immediately, do not spend time retrying `codex exec` first.**
11
+
12
+ Bug-fix WO on CHART-2/CHART-3. Found live in jg-ferien's "Aktivität" screen (ACT-3/ACT-5 consumer)
13
+ immediately after CHART-3 shipped and jg's pin was bumped: the toolbar, series checkboxes, and MUI
14
+ X-Charts **legend** now all render correctly (proving CHART-3's fix works and real data is present),
15
+ but the chart canvas itself — bars and axes — is entirely absent. The card ends right after the
16
+ legend row with visible empty space beneath it.
17
+
18
+ ## TIER
19
+ Tier 2 — shared-core UI consumed by every app that adopts `TimeSeriesChart`.
20
+ Independent `reviewer` + `ui_reviewer` mandatory.
21
+
22
+ ## WHY
23
+
24
+ `TimeSeriesChart` composes `ChartFrame` + `BarChart` but never supplies either with a height:
25
+
26
+ ```jsx
27
+ <ChartFrame title={title} subtitle={subtitle} toolbar={toolbar} loading={loading} error={error} isEmpty={isDataEmpty}>
28
+ {!isDataEmpty && (
29
+ <BarChart xAxisLabel={xAxisLabel} yAxisLabel={yAxisLabel} xAxis={[{ data: data?.xLabels || [] }]}
30
+ series={visibleSeries.map(...)} palette={visiblePalette} />
31
+ )}
32
+ </ChartFrame>
33
+ ```
34
+
35
+ Neither `minHeight`/`aspect` (ChartFrame's own sizing props) nor `minHeight`/`aspect`/a raw MUI
36
+ `height` (BarChart's) are passed. `ChartFrame`'s inner content `Box` has `display: flex, alignItems:
37
+ center, justifyContent: center` with no defined height — with `alignItems: center` (not `stretch`),
38
+ a flex child gets its own intrinsic height, not the parent's. `BarChart`'s own wrapping `Box` (`sx:
39
+ {width:'100%', minHeight, aspectRatio}`) therefore also has no height when neither prop is supplied.
40
+ MUI X-Charts' internal responsive container measures its actual parent height via `ResizeObserver` to
41
+ size the SVG — with a zero-height parent, the chart canvas (bars, axes) renders at zero size. The
42
+ **legend** is a separate MUI X-Charts sub-component sized by its own content (text + swatches), so it
43
+ renders regardless of container height — which is exactly why the legend was the only visible sign
44
+ of a mounted chart in the live screenshot.
45
+
46
+ This gap was invisible until now because CHART-3's own bug (`visibleKeys` never populating) kept
47
+ `isDataEmpty` permanently `true`, so `BarChart` never actually mounted in any live deployment before
48
+ today. It also went uncaught by CHART-2's and CHART-3's own tests: both `tests/BarChart.test.jsx` and
49
+ `tests/TimeSeriesChart.test.jsx` mock `@mui/x-charts/BarChart` itself (`vi.mock('@mui/x-charts/BarChart', ...)`
50
+ per CHART-2's own required-tests convention), so no test in this repo has ever mounted the *real* MUI
51
+ chart and observed its rendered size. The working reference is `dev/entries.jsx`'s `BarChartEntry`
52
+ (DX-1 harness), which explicitly sets `minHeight={360}` on `ChartFrame` and `height={320}` directly
53
+ on `BarChart` — i.e. every other real consumer of these primitives in this repo already knows it must
54
+ supply a height; `TimeSeriesChart`, as a preset built on top, was supposed to supply this itself
55
+ (hosts don't see `ChartFrame`/`BarChart` directly through the preset) and doesn't.
56
+
57
+ ## SCOPE
58
+
59
+ **Give `TimeSeriesChart` a sensible built-in height for the chart it composes**, matching the
60
+ DX-1-proven working pattern:
61
+ 1. Pass `minHeight` to `ChartFrame` (sizes the loading/error/empty states too, not just the chart).
62
+ 2. Pass a height down to `BarChart` so the actual MUI chart canvas gets a real, non-zero pixel size —
63
+ either `BarChart`'s own `minHeight`/`aspect` wrapper props, or a raw `height` forwarded via
64
+ `BarChart`'s `...chartProps` pass-through to `MuiBarChart` (the mechanism `dev/entries.jsx` uses
65
+ directly against `BarChart`, confirmed working there). Pick whichever actually renders a non-zero
66
+ chart canvas when verified (see REQUIRED TESTS) — do not assume `aspectRatio` CSS alone reliably
67
+ drives MUI X-Charts' `ResizeObserver` sizing without checking; the raw `height` prop is the
68
+ confirmed-working mechanism in this repo's own reference usage and is the safer default if there
69
+ is any doubt.
70
+ 3. Pick one fixed value (e.g. `320`/`360`, matching `dev/entries.jsx`'s own numbers) as a preset
71
+ default — this is a preset, not a raw primitive; the host does not choose chart height, matching
72
+ `TimeSeriesChart`'s existing "no new props" presentational contract. Do not add a new prop for
73
+ this unless a reviewer specifically asks for one.
74
+
75
+ **This WO's real fix is the test gap, not just the pixels.** The reason this shipped broken twice is
76
+ that every test in this file mocks the real MUI chart component. Required test 1 below must exercise
77
+ the **unmocked** `@mui/x-charts/BarChart` and assert a real rendered size — a mocked-chart test would
78
+ be exactly the kind of vacuous coverage that let this ship.
79
+
80
+ ## NON-GOALS / DO NOT TOUCH
81
+ - Do not change the range picker, series toggles, `chartsTranslations`, or the CHART-3 `visibleKeys`
82
+ sync logic — this WO is scoped to sizing only.
83
+ - Do not change `ChartFrame`'s or `BarChart`'s own prop contracts (`minHeight`/`aspect` already exist
84
+ on both) — only how `TimeSeriesChart` calls them.
85
+ - Do not add a new prop to `TimeSeriesChart`'s public API for this — see SCOPE point 3.
86
+
87
+ ## RISKS
88
+ - Every host that has adopted `TimeSeriesChart` is affected (currently jg-ferien via ACT-3/ACT-5) —
89
+ this changes rendering behaviour for all of them, in the correct direction (chart canvas now
90
+ actually visible).
91
+ - Getting this wrong a second time (e.g. an `aspectRatio`-only fix that still doesn't reliably drive
92
+ MUI X-Charts' internal measurement) ships a third silent failure in the same component. The
93
+ required unmocked-render test exists specifically to catch that before it reaches a live app again.
94
+
95
+ ## REQUIRED TESTS TO WRITE
96
+ Extend `tests/TimeSeriesChart.test.jsx`. Do NOT run the full suite.
97
+
98
+ 1. **The real bug's mechanism, not a jsdom layout measurement.** An initial attempt at this WO wrote
99
+ an *unmocked* test (real `@mui/x-charts/BarChart`, no `vi.mock`) asserting the rendered chart
100
+ surface's `viewBox` height was non-zero — verified, in practice, to be **vacuous**: it stayed green
101
+ even with the pre-fix code (no `height` prop anywhere), because jsdom's `ResizeObserver`/layout
102
+ handling does not reproduce the real-browser zero-height-parent behaviour this bug depends on. Do
103
+ not repeat that approach. Instead, keep the existing `chartSpy` mock convention (`vi.hoisted` +
104
+ `vi.mock('@mui/x-charts/BarChart', ...)`, already at `tests/TimeSeriesChart.test.jsx:9-10`) and
105
+ assert directly on the props the real `MuiBarChart` receives: `chartSpy.mock.calls.at(-1)[0].height`
106
+ must be a real, non-zero number. This is what actually distinguishes fixed from broken — confirmed
107
+ by reverting the fix and re-running: the prop assertion fails pre-fix (`props.height` is
108
+ `undefined`), the unmocked viewBox assertion does not.
109
+ 2. **Non-vacuity**: this test must fail against the current (pre-fix) code — verify before considering
110
+ the fix complete (see note above — already verified once for the prop-assertion approach).
111
+ 3. Existing mocked-`BarChart` tests (range/toggle/empty-state behaviour) stay as they are.
112
+
113
+ ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
114
+
115
+ Work from this package; do not explore broadly from scratch — open only the named files to verify.
116
+
117
+ **File to change: `src/components/charts/TimeSeriesChart.jsx`.**
118
+
119
+ Current return statement (verified, full slice, `:150-169`):
120
+ ```jsx
121
+ return (
122
+ <ChartFrame
123
+ title={title}
124
+ subtitle={subtitle}
125
+ toolbar={toolbar}
126
+ loading={loading}
127
+ error={error}
128
+ isEmpty={isDataEmpty}
129
+ >
130
+ {!isDataEmpty && (
131
+ <BarChart
132
+ xAxisLabel={xAxisLabel}
133
+ yAxisLabel={yAxisLabel}
134
+ xAxis={[{ data: data?.xLabels || [] }]}
135
+ series={visibleSeries.map((series) => ({ data: series.data, label: series.label }))}
136
+ palette={visiblePalette}
137
+ />
138
+ )}
139
+ </ChartFrame>
140
+ );
141
+ ```
142
+
143
+ **Reference (working) pattern — `dev/entries.jsx:35-39`:**
144
+ ```jsx
145
+ function BarChartEntry() {
146
+ return (
147
+ <ChartFrame title="Monthly cases" subtitle="Standalone BarChart entry" minHeight={360}>
148
+ <BarChart {...barChartFixture} xAxisLabel="Month" yAxisLabel="Cases" height={320} />
149
+ </ChartFrame>
150
+ );
151
+ }
152
+ ```
153
+ Note `height={320}` on `BarChart` here is NOT `BarChart`'s own `minHeight`/`aspect` wrapper prop — it
154
+ lands in `BarChart`'s `...chartProps` rest spread (`BarChart.jsx:24,34`) and is forwarded straight to
155
+ `MuiBarChart`'s own native `height` prop, which MUI X-Charts uses as a fixed pixel height instead of
156
+ measuring the parent via `ResizeObserver`. This is the confirmed-working mechanism in this repo.
157
+
158
+ **`ChartFrame.jsx:86-99`** — the content `Box`: `sx={{ width:'100%', minHeight, aspectRatio: aspect,
159
+ display:'flex', alignItems:'center', justifyContent:'center' }}`. `alignItems:'center'` means a flex
160
+ child (here, `BarChart`'s own `Box`) is NOT stretched to fill this Box's height — it keeps its own
161
+ intrinsic height. Passing `minHeight` to `ChartFrame` sizes this outer Box correctly for the
162
+ loading/error/empty `Alert`/`CircularProgress` states (which have no fixed height of their own and
163
+ DO benefit from the flex-centered parent height), but does **not**, by itself, give `BarChart`'s
164
+ child `Box` any height — that must come from `BarChart` itself (its own `minHeight`/`aspect`, or the
165
+ raw `height` chartProp per the reference pattern above).
166
+
167
+ **`BarChart.jsx:15-44`** — full file, already shown above in this WO's WHY section — `minHeight`/
168
+ `aspect` are explicit named props (`:22-23`) applied to `BarChart`'s own wrapping `Box` (`:32`);
169
+ anything else lands in `...chartProps` (`:24`) and is spread onto `MuiBarChart` directly (`:34`),
170
+ which is how a raw `height` reaches the underlying MUI component.
171
+
172
+ **Test file: `tests/TimeSeriesChart.test.jsx`** — already mocks `@mui/x-charts/BarChart` via
173
+ `vi.hoisted` + `vi.mock` (`:9-10`) and every existing test reads `chartSpy.mock.calls.at(-1)[0]` to
174
+ assert exact props (e.g. the `series`/`colors` assertions at `:87-89,94-99`). Add the new test in the
175
+ same style, asserting `props.height` — see REQUIRED TESTS above for why an unmocked render test does
176
+ NOT work here.
177
+
178
+ ## TARGET REPO
179
+ `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release
180
+ flow (patch bump — bug fix, no interface change).
181
+
182
+ ## TEST SCOPE FOR THE GATE (orchestrator)
183
+ `tests/TimeSeriesChart.test.jsx`. Not the full suite.
184
+
185
+ ## MINI-HANDOVER (pastable)
186
+
187
+ > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`).
188
+ > Work order: `work-orders/CHART-4.md` — read it fully, then follow the `orchestrate-codex` skill.
189
+ > **Codex workspace is out of credits this session — go straight to the direct-Claude fallback, do
190
+ > not attempt `codex exec` first.**
191
+ >
192
+ > Second bug in `TimeSeriesChart` (after CHART-3): the chart legend renders but the actual bars/axes
193
+ > never appear because neither `ChartFrame` nor `BarChart` ever receive a height — MUI X-Charts'
194
+ > responsive container measures a zero-height parent and draws nothing. `dev/entries.jsx`'s
195
+ > `BarChartEntry` is the proof this is fixable: `minHeight={360}` on `ChartFrame` + `height={320}`
196
+ > forwarded straight to `BarChart` (MUI's own native height prop, not the wrapper's `minHeight`/
197
+ > `aspect`). The required regression test must NOT mock `@mui/x-charts/BarChart` — every existing test
198
+ > in this component mocks it, which is exactly how two sizing/rendering bugs shipped undetected.
199
+
200
+ ## PROGRESS CONTRACT
201
+ Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
202
+ relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
203
+ stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.