@micha.bigler/ui-core-micha 3.14.4 → 4.0.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.
Files changed (341) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +7 -0
  3. package/dist/chat/kitSxRegistry.js +9 -0
  4. package/dist/components/charts/kitSxRegistry.js +6 -0
  5. package/dist/entries/charts.js +6 -0
  6. package/dist/entries/chat.js +8 -0
  7. package/dist/entries/markdown.js +1 -0
  8. package/dist/index.js +0 -14
  9. package/dist/pages/AccountPage.js +1 -2
  10. package/dist/pages/LoginPage.js +1 -2
  11. package/dist/pages/PasswordChangePage.js +1 -2
  12. package/dist/pages/PasswordInvitePage.js +1 -2
  13. package/dist/pages/PasswordResetRequestPage.js +1 -2
  14. package/dist/pages/SignUpPage.js +1 -2
  15. package/dist/pages/SignupConfirmPage.js +1 -2
  16. package/dist/theme/kitSxEntry.js +11 -0
  17. package/dist/theme/kitSxRegistry.js +2 -15
  18. package/package.json +32 -7
  19. package/.github/workflows/ci.yml +0 -35
  20. package/.github/workflows/publish.yml +0 -126
  21. package/DESIGN.md +0 -60
  22. package/WORK_ORDERS.md +0 -150
  23. package/dev/entries.jsx +0 -721
  24. package/dev/fixtures.js +0 -26
  25. package/dev/main.jsx +0 -75
  26. package/dev/mockTransport.jsx +0 -128
  27. package/docs/CHART-LAYOUT.md +0 -157
  28. package/docs/DARK-TOKENS.md +0 -140
  29. package/docs/TYPE-SCALE.md +0 -111
  30. package/docs/messaging-deviations.md +0 -118
  31. package/index.html +0 -12
  32. package/renovate.json +0 -10
  33. package/scripts/chart-api-census.mjs +0 -415
  34. package/src/auth/AuthContext.jsx +0 -158
  35. package/src/auth/ReauthModal.jsx +0 -82
  36. package/src/auth/UserMenu.jsx +0 -121
  37. package/src/auth/apiClient.jsx +0 -232
  38. package/src/auth/authApi.jsx +0 -539
  39. package/src/auth/authConfig.jsx +0 -34
  40. package/src/auth/reauth.js +0 -47
  41. package/src/chat/ChatComposer.jsx +0 -36
  42. package/src/chat/ChatList.jsx +0 -87
  43. package/src/chat/ChatThread.jsx +0 -128
  44. package/src/chat/ChatWorkspace.jsx +0 -244
  45. package/src/chat/MarkdownReply.jsx +0 -22
  46. package/src/chat/ModelPicker.jsx +0 -85
  47. package/src/chat/api.js +0 -18
  48. package/src/chat/useChatSocket.js +0 -32
  49. package/src/chat/wire.js +0 -7
  50. package/src/components/AccessCodeManager.jsx +0 -288
  51. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  52. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  53. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  54. package/src/components/BulkInviteCsvTab.jsx +0 -198
  55. package/src/components/EmptyState.jsx +0 -38
  56. package/src/components/LoginForm.jsx +0 -159
  57. package/src/components/MFAComponent.jsx +0 -350
  58. package/src/components/MfaLoginComponent.jsx +0 -252
  59. package/src/components/MobileBottomNav.jsx +0 -82
  60. package/src/components/PasskeysComponent.jsx +0 -252
  61. package/src/components/PasswordChangeForm.jsx +0 -87
  62. package/src/components/PasswordResetRequestForm.jsx +0 -43
  63. package/src/components/PasswordSetForm.jsx +0 -97
  64. package/src/components/ProfileComponent.jsx +0 -348
  65. package/src/components/QrSignupManager.jsx +0 -404
  66. package/src/components/QrSignupValidityManager.jsx +0 -106
  67. package/src/components/RegistrationMethodsManager.jsx +0 -150
  68. package/src/components/SecurityComponent.jsx +0 -141
  69. package/src/components/SocialLoginButtons.jsx +0 -83
  70. package/src/components/SoftChip.jsx +0 -93
  71. package/src/components/StatTile.jsx +0 -39
  72. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  73. package/src/components/TurnstileWidget.jsx +0 -99
  74. package/src/components/UserInviteComponent.jsx +0 -76
  75. package/src/components/UserListComponent.jsx +0 -618
  76. package/src/components/charts/BarChart.jsx +0 -102
  77. package/src/components/charts/ChartFrame.jsx +0 -214
  78. package/src/components/charts/LineChart.jsx +0 -126
  79. package/src/components/charts/ScatterChart.jsx +0 -299
  80. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  81. package/src/components/charts/chartDefaults.js +0 -644
  82. package/src/components/charts/chartLabels.js +0 -36
  83. package/src/components/charts/exportChart.js +0 -258
  84. package/src/components/charts/formatters.js +0 -39
  85. package/src/components/charts/palette.js +0 -33
  86. package/src/components/charts/yearTickInterval.js +0 -7
  87. package/src/i18n/authTranslations.ts +0 -2651
  88. package/src/i18n/chartsTranslations.ts +0 -91
  89. package/src/i18n/chatTranslations.ts +0 -55
  90. package/src/i18n/messagingTranslations.ts +0 -120
  91. package/src/i18n/notificationsTranslations.ts +0 -32
  92. package/src/i18n/onboardingTranslations.ts +0 -29
  93. package/src/i18n/sectionNavTranslations.ts +0 -20
  94. package/src/i18n/uiCoreTranslations.js +0 -29
  95. package/src/i18n/userMenuTranslations.ts +0 -5
  96. package/src/index.js +0 -158
  97. package/src/layout/PageLayout.jsx +0 -36
  98. package/src/layout/SectionNav.jsx +0 -320
  99. package/src/messaging/AttachmentList.jsx +0 -145
  100. package/src/messaging/Composer.jsx +0 -197
  101. package/src/messaging/ConversationLaunchers.jsx +0 -51
  102. package/src/messaging/ConversationList.jsx +0 -109
  103. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  104. package/src/messaging/MessageBubble.jsx +0 -183
  105. package/src/messaging/MessagingProvider.jsx +0 -0
  106. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  107. package/src/messaging/PollCard.jsx +0 -163
  108. package/src/messaging/ReactionBar.jsx +0 -43
  109. package/src/messaging/ReadTicks.jsx +0 -82
  110. package/src/messaging/Thread.jsx +0 -195
  111. package/src/messaging/api.js +0 -48
  112. package/src/messaging/composerImageCompression.js +0 -35
  113. package/src/messaging/linkifyText.jsx +0 -37
  114. package/src/notifications/NotificationBell.jsx +0 -84
  115. package/src/notifications/NotificationSettings.jsx +0 -330
  116. package/src/notifications/NotificationsProvider.jsx +0 -224
  117. package/src/notifications/PopupSurface.jsx +0 -105
  118. package/src/notifications/api.js +0 -55
  119. package/src/notifications/feedApi.js +0 -18
  120. package/src/notifications/realtime.jsx +0 -138
  121. package/src/notifications/serviceWorker/sw.js +0 -50
  122. package/src/onboarding/OnboardingProvider.jsx +0 -326
  123. package/src/onboarding/OnboardingWizard.jsx +0 -61
  124. package/src/onboarding/WizardDialogShell.jsx +0 -51
  125. package/src/onboarding/api.js +0 -13
  126. package/src/onboarding/stepSelection.js +0 -16
  127. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  128. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  129. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  130. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  131. package/src/pages/AccountPage.jsx +0 -394
  132. package/src/pages/LoginPage.jsx +0 -343
  133. package/src/pages/PasswordChangePage.jsx +0 -62
  134. package/src/pages/PasswordInvitePage.jsx +0 -177
  135. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  136. package/src/pages/SignUpPage.jsx +0 -268
  137. package/src/pages/SignupConfirmPage.jsx +0 -155
  138. package/src/testing/checkKitIntegration.js +0 -233
  139. package/src/theme/createAppTheme.js +0 -423
  140. package/src/theme/fonts.js +0 -3
  141. package/src/theme/index.js +0 -21
  142. package/src/theme/kitSxRegistry.js +0 -285
  143. package/src/theme/themeCompleteness.js +0 -1045
  144. package/src/theme/tokens.js +0 -478
  145. package/src/utils/auth-errors.js +0 -76
  146. package/src/utils/authService.js +0 -223
  147. package/src/utils/resolveErrorText.js +0 -28
  148. package/src/utils/webauthn.js +0 -51
  149. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  150. package/tests/AccountPage.test.jsx +0 -200
  151. package/tests/AuthContext.test.jsx +0 -129
  152. package/tests/BarChart.test.jsx +0 -112
  153. package/tests/BrowserPushStep.test.jsx +0 -110
  154. package/tests/ChartFrame.test.jsx +0 -336
  155. package/tests/ConversationList.test.jsx +0 -144
  156. package/tests/CookieConsentStep.test.jsx +0 -69
  157. package/tests/EmptyState.test.jsx +0 -62
  158. package/tests/LineChart.test.jsx +0 -80
  159. package/tests/MessagingProvider.test.jsx +0 -50
  160. package/tests/MobileBottomNav.test.jsx +0 -319
  161. package/tests/NotificationBell.test.jsx +0 -60
  162. package/tests/NotificationSettings.test.jsx +0 -276
  163. package/tests/NotificationsProvider.test.jsx +0 -240
  164. package/tests/OnboardingProvider.test.jsx +0 -294
  165. package/tests/OnboardingWizard.test.jsx +0 -143
  166. package/tests/PasswordChangeForm.test.jsx +0 -70
  167. package/tests/PasswordInvitePage.test.jsx +0 -142
  168. package/tests/PasswordSetForm.test.jsx +0 -70
  169. package/tests/PopupSurface.test.jsx +0 -142
  170. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  171. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  172. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  173. package/tests/ScatterChart.test.jsx +0 -270
  174. package/tests/SectionNav.test.jsx +0 -363
  175. package/tests/SignUpPage.test.jsx +0 -208
  176. package/tests/SoftChip.test.jsx +0 -105
  177. package/tests/StatTile.test.jsx +0 -57
  178. package/tests/Thread.test.jsx +0 -151
  179. package/tests/TimeSeriesChart.test.jsx +0 -367
  180. package/tests/UserListComponent.test.jsx +0 -192
  181. package/tests/UserMenu.test.jsx +0 -171
  182. package/tests/apiClientLanguage.test.js +0 -33
  183. package/tests/apiClientPublicPaths.test.js +0 -178
  184. package/tests/authApiLogin.test.js +0 -42
  185. package/tests/authApiRegistration.test.js +0 -31
  186. package/tests/authErrorCodeRendering.test.jsx +0 -77
  187. package/tests/authErrors.test.js +0 -74
  188. package/tests/authTranslations.test.js +0 -74
  189. package/tests/chart-api-census.test.js +0 -366
  190. package/tests/chartAxisTickBand.test.jsx +0 -95
  191. package/tests/chartDefaults.test.js +0 -902
  192. package/tests/chartLabels.test.js +0 -50
  193. package/tests/chartsPalette.test.js +0 -99
  194. package/tests/chatApi.test.js +0 -48
  195. package/tests/chatDriverNames.test.jsx +0 -71
  196. package/tests/chatExports.test.js +0 -3
  197. package/tests/chatList.test.jsx +0 -84
  198. package/tests/chatPicker.test.jsx +0 -82
  199. package/tests/chatSocket.test.jsx +0 -26
  200. package/tests/chatThread.test.jsx +0 -116
  201. package/tests/chatTranslations.test.js +0 -47
  202. package/tests/chatWorkspace.test.jsx +0 -234
  203. package/tests/checkKitIntegration.test.jsx +0 -219
  204. package/tests/createAppTheme.test.js +0 -132
  205. package/tests/createAppThemeDark.test.js +0 -127
  206. package/tests/createAppThemeLightGuard.test.js +0 -40
  207. package/tests/createAppThemePageContrast.test.js +0 -108
  208. package/tests/createAppThemeTypography.test.js +0 -53
  209. package/tests/errorCallSiteGuard.test.js +0 -32
  210. package/tests/exportChart.test.js +0 -313
  211. package/tests/feedApi.test.js +0 -46
  212. package/tests/fixtures/light-theme-baseline.json +0 -2234
  213. package/tests/i18nAggregate.test.js +0 -120
  214. package/tests/i18nGermanVariant.test.js +0 -65
  215. package/tests/loginPageErrorResolution.test.jsx +0 -102
  216. package/tests/messagingApi.test.js +0 -32
  217. package/tests/messagingChunk6.test.jsx +0 -116
  218. package/tests/messagingCompactBubble.test.jsx +0 -64
  219. package/tests/messagingComposer.test.jsx +0 -136
  220. package/tests/messagingComposerChunk5.test.jsx +0 -113
  221. package/tests/messagingContractConformance.test.js +0 -69
  222. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  223. package/tests/messagingExports.test.js +0 -21
  224. package/tests/messagingInteractions.test.jsx +0 -136
  225. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  226. package/tests/messagingLinkify.test.jsx +0 -51
  227. package/tests/messagingMessageActions.test.jsx +0 -163
  228. package/tests/messagingMsg6.test.jsx +0 -105
  229. package/tests/messagingMsg6f.test.jsx +0 -155
  230. package/tests/messagingMsg6g.test.jsx +0 -166
  231. package/tests/messagingMsg6h.test.jsx +0 -136
  232. package/tests/messagingMsg6i.test.jsx +0 -129
  233. package/tests/messagingPollRendering.test.jsx +0 -142
  234. package/tests/messagingReactionMerge.test.jsx +0 -77
  235. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  236. package/tests/messagingThreadReceipts.test.jsx +0 -178
  237. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  238. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  239. package/tests/notificationsApi.test.js +0 -41
  240. package/tests/notificationsExports.test.js +0 -34
  241. package/tests/onboardingDescriptors.test.js +0 -132
  242. package/tests/packageTreeShaking.test.js +0 -126
  243. package/tests/realtime.test.jsx +0 -219
  244. package/tests/resolveErrorText.test.js +0 -72
  245. package/tests/stepSelection.test.js +0 -53
  246. package/tests/themeCompleteness.test.js +0 -626
  247. package/tests/timeSeriesLegend.test.js +0 -99
  248. package/tests/userScreenTranslations.test.jsx +0 -218
  249. package/tsconfig.build.json +0 -18
  250. package/vite.config.mjs +0 -6
  251. package/work-orders/AUTH-1.md +0 -74
  252. package/work-orders/AUTH-2.md +0 -74
  253. package/work-orders/AUTH-4.md +0 -183
  254. package/work-orders/AUTH-5.md +0 -154
  255. package/work-orders/AUTH-7.md +0 -181
  256. package/work-orders/AUTH-8.md +0 -184
  257. package/work-orders/CHART-1.md +0 -285
  258. package/work-orders/CHART-2.md +0 -222
  259. package/work-orders/CHART-3.md +0 -163
  260. package/work-orders/CHART-4.md +0 -203
  261. package/work-orders/CHART-5.md +0 -226
  262. package/work-orders/CHART-6.md +0 -428
  263. package/work-orders/CHART-7.md +0 -175
  264. package/work-orders/DX-1.md +0 -169
  265. package/work-orders/DX-2.md +0 -188
  266. package/work-orders/DX-3.md +0 -195
  267. package/work-orders/I18N-1.md +0 -254
  268. package/work-orders/I18N-3.md +0 -207
  269. package/work-orders/MSG-3.md +0 -375
  270. package/work-orders/MSG-3b.md +0 -1002
  271. package/work-orders/MSG-3c.md +0 -378
  272. package/work-orders/MSG-3d.md +0 -268
  273. package/work-orders/MSG-3e.md +0 -164
  274. package/work-orders/MSG-3f.md +0 -391
  275. package/work-orders/MSG-6e.md +0 -308
  276. package/work-orders/MSG-6f.md +0 -214
  277. package/work-orders/MSG-6g.md +0 -244
  278. package/work-orders/MSG-6h.md +0 -238
  279. package/work-orders/MSG-6i.md +0 -271
  280. package/work-orders/MSG-6j.md +0 -117
  281. package/work-orders/NOTIF-12.md +0 -214
  282. package/work-orders/NOTIF-13.md +0 -142
  283. package/work-orders/NOTIF-14.md +0 -151
  284. package/work-orders/PRIM-1.md +0 -177
  285. package/work-orders/SHELL-1.md +0 -255
  286. package/work-orders/SHELL-2.md +0 -197
  287. package/work-orders/SHELL-3.md +0 -359
  288. package/work-orders/SHELL-4.md +0 -351
  289. package/work-orders/SHELL-5.md +0 -345
  290. package/work-orders/SHELL-6.md +0 -210
  291. package/work-orders/THEME-1.md +0 -587
  292. package/work-orders/THEME-10.md +0 -303
  293. package/work-orders/THEME-11.md +0 -208
  294. package/work-orders/THEME-2.md +0 -339
  295. package/work-orders/THEME-3.md +0 -210
  296. package/work-orders/THEME-4.md +0 -271
  297. package/work-orders/THEME-5.md +0 -341
  298. package/work-orders/THEME-6.md +0 -247
  299. package/work-orders/THEME-7.md +0 -82
  300. package/work-orders/THEME-8.md +0 -211
  301. package/work-orders/THEME-9.md +0 -194
  302. package/work-orders/UCM-AUTH-10.md +0 -490
  303. package/work-orders/UCM-AUTH-11.md +0 -185
  304. package/work-orders/UCM-AUTH-12.md +0 -98
  305. package/work-orders/UCM-AUTH-13.md +0 -47
  306. package/work-orders/UCM-AUTH-9.md +0 -207
  307. package/work-orders/UCM-CHART-10.md +0 -150
  308. package/work-orders/UCM-CHART-11.md +0 -132
  309. package/work-orders/UCM-CHART-12.md +0 -348
  310. package/work-orders/UCM-CHART-13.md +0 -122
  311. package/work-orders/UCM-CHART-14.md +0 -198
  312. package/work-orders/UCM-CHART-15.md +0 -187
  313. package/work-orders/UCM-CHART-16.md +0 -169
  314. package/work-orders/UCM-CHART-17.md +0 -195
  315. package/work-orders/UCM-CHART-18.md +0 -200
  316. package/work-orders/UCM-CHART-8.md +0 -147
  317. package/work-orders/UCM-CHART-9.md +0 -265
  318. package/work-orders/UCM-CHAT-1.md +0 -190
  319. package/work-orders/UCM-CHAT-2.md +0 -75
  320. package/work-orders/UCM-CI-1.md +0 -65
  321. package/work-orders/UCM-DEP-1.md +0 -63
  322. package/work-orders/UCM-I18N-4.md +0 -226
  323. package/work-orders/UCM-I18N-5.md +0 -211
  324. package/work-orders/UCM-MSG-20.md +0 -192
  325. package/work-orders/UCM-PRIM-2.md +0 -114
  326. package/work-orders/UCM-SHELL-7.md +0 -134
  327. package/work-orders/UCM-SHELL-8.md +0 -125
  328. package/work-orders/UCM-TEST-1.md +0 -304
  329. package/work-orders/UCM-THEME-13.md +0 -69
  330. package/work-orders/UCM-THEME-14.md +0 -312
  331. package/work-orders/UCM-THEME-15.md +0 -356
  332. package/work-orders/UCM-THEME-16.md +0 -80
  333. package/work-orders/UCM-THEME-17.md +0 -111
  334. package/work-orders/UCM-THEME-18.md +0 -184
  335. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  336. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  337. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  338. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  339. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  340. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  341. package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
@@ -1,222 +0,0 @@
1
- # WORK ORDER CHART-2 (ui-core-micha) — a time-series chart preset with range picker and series toggles
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
-
10
- Feature-level WO (Expertenchat envelope). Extends CHART-1. Operator request 2026-08-04 from jg's event
11
- activity chart; the same controls are wanted wherever a time series is charted, and `django-core-micha`
12
- ACT-1 makes activity data available to every app.
13
-
14
- ## TIER
15
- Tier 2 — shared-core UI consumed by every app. Independent `reviewer` + `ui_reviewer` mandatory.
16
-
17
- ## WHY
18
-
19
- CHART-1 already ships `ChartFrame` (Paper + title + **toolbar slot** + loading / error / empty +
20
- responsive container + SVG/PNG export + a11y), a themed `BarChart`, `getNeutralChartPalette` and
21
- `chartsTranslations`. The frame's toolbar slot exists for exactly this and is currently unused by any
22
- consumer.
23
-
24
- jg's `ActivitySection.jsx` bypasses the kit entirely (`import { BarChart } from '@mui/x-charts/BarChart'`
25
- at `:9`) and hand-rolls its own `Paper`, loading, error and empty states. Rather than jg building a
26
- range picker and series checkboxes locally, they belong in the kit — every app charting a time series
27
- wants both.
28
-
29
- ## SCOPE
30
-
31
- **A. A range picker for the toolbar slot.** Presets **1 day / 1 week / 1 month / 1 year** (operator,
32
- 2026-08-04), emitting the selected range to the host. The host owns fetching; the picker owns the choice.
33
-
34
- The range must also carry the **granularity** the consumer should request — dcm ACT-1's query endpoint
35
- aggregates server-side, and a year of 4-hour buckets is ~2190 points, which no chart can render
36
- usefully. A picker that changes the window but not the resolution reproduces exactly the
37
- unreadable-density problem this WO exists to fix.
38
-
39
- **The range-to-resolution mapping (operator, 2026-08-04) — implement exactly this:**
40
-
41
- | Range | Resolution | Points |
42
- |---|---|---|
43
- | 1 day | 1 hour | 24 |
44
- | 1 week | 4 hours | ~42 |
45
- | 1 month | 1 day | ~30 |
46
- | 1 year | 1 month | 12 |
47
-
48
- Every preset lands between a dozen and fifty points, which is what keeps the chart readable at any
49
- range. **The resolution follows the window; it is not an independent control** and must not be exposed
50
- as one.
51
-
52
-
53
- **B. Series toggles for the toolbar slot.** Check/uncheck each series independently.
54
-
55
- **These are not cosmetic.** In jg's chart two series share one y-axis with different units — user counts
56
- reach 28 while presence-hours sit near zero, so the second series is invisible. Toggling one off
57
- rescales the axis and makes the other readable. Whether to offer a second y-axis instead is a real
58
- alternative: **if you do not add one, say so deliberately** rather than leaving it unconsidered.
59
-
60
- Deselecting every series must leave a stable empty chart — not a crash, not a permanent spinner.
61
-
62
- **C. Compose them into a preset** on `ChartFrame` + the existing `BarChart`, so a consumer gets frame,
63
- chart, picker and toggles from one component and passes data plus callbacks.
64
-
65
- **Presentational only — the host fetches.** Take `data`, the current range and series selection, and
66
- callbacks; do not call any endpoint. If all four hosts later write the identical fetch against ACT-1's
67
- endpoint, *that* is the signal to add an adapter — and note the inverse trap from this estate's recent
68
- history: building the shared thing before two identical consumers exist is how you generalise against a
69
- single case and get it wrong.
70
-
71
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
72
-
73
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
74
-
75
- **New file: `src/components/charts/TimeSeriesChart.jsx`** (generic name — non-goal bars anything
76
- app-specific; this is not called `ActivityChart`). Export it in `src/index.js` right after the
77
- existing `LineChart` export (`:46`): `export { TimeSeriesChart } from './components/charts/TimeSeriesChart';`.
78
-
79
- **`ChartFrame`'s toolbar slot (`src/components/charts/ChartFrame.jsx`)** — the prop is named
80
- **`toolbar`** (not `toolbarSlot`), destructured `:20`, rendered `:74` inside a `Box` next to the
81
- title/subtitle, `justifyContent: 'space-between'`. Full prop list `:17-34`:
82
- `title, subtitle, toolbar, loading, error, isEmpty, emptyMessage, minHeight, aspect, exportOptions,
83
- onExportSvg, onExportPng, ariaLabel, children, variant, sx`. State precedence `:58-65`:
84
- `loading → error → isEmpty → children` — `TimeSeriesChart` passes its own `loading`/`error`/`isEmpty`
85
- straight through, it does not reimplement these.
86
-
87
- **`BarChart` (`src/components/charts/BarChart.jsx`, 44 lines)** — required props `xAxisLabel`/
88
- `yAxisLabel` (throws if absent, `:27-29`). `series` is a bare array of MUI X-Charts series objects,
89
- e.g. `{ data: [...], label, ... }` — no ucm wrapping. Colors: `palette || neutralPalette.categorical`
90
- (from `useNeutralChartPalette()`), assigned **positionally by index** — series `i` gets
91
- `categorical[i]`. `hideLegend={series.length <= 1}`.
92
-
93
- **Data contract for `TimeSeriesChart`** (design decision, state it): the host has already fetched
94
- and shaped the series (dcm ACT-1's query endpoint returns rolled-up buckets; this component knows
95
- nothing about that origin). Prop shape:
96
- ```jsx
97
- <TimeSeriesChart
98
- title
99
- xAxisLabel
100
- yAxisLabel
101
- data={{ xLabels: [...], series: [{ key: 'users', label: 'Users', data: [...] }, { key: 'presence', label: 'Presence', data: [...] }] }}
102
- loading
103
- error
104
- onRangeChange={(rangeKey, granularity) => {...}} // rangeKey: '1d'|'1w'|'1m'|'1y'
105
- />
106
- ```
107
- Range selection is **self-managed internal state** (default a sensible preset, e.g. `'1w'`), emitting
108
- `onRangeChange(rangeKey, granularity)` on every change so the host can refetch `data` for the new
109
- window — the host owns fetching (non-goal), this component owns only which range is currently
110
- selected and telling the host what changed. Series-toggle visibility is **also self-managed internal
111
- state** (`Set` of visible series keys, all visible by default) — deselecting is a pure client-side
112
- filter over already-fetched `data`, no host round-trip needed, so no `onSeriesToggle` callback is
113
- required (optional to add if a reviewer disagrees, but the WO's own framing — "these are not
114
- cosmetic... toggling rescales the axis" — describes a display-only operation).
115
-
116
- **Range-to-granularity mapping — implement exactly, no per-app override:**
117
- `'1d' → 'hour'`, `'1w' → '4hour'`, `'1m' → 'day'`, `'1y' → 'month'` (match whatever granularity
118
- vocabulary dcm ACT-1's query endpoint expects once it publishes — since CHART-2 does not fetch, the
119
- exact string values are this component's own contract, name them clearly, e.g. `'hour' | '4hour' |
120
- 'day' | 'month'`, and state the chosen vocabulary in the completion note so ACT-1/ACT-3 can align).
121
-
122
- **Range picker widget — decide and state (per WO's own "if you consider X, say so"):** this repo has
123
- **zero existing `ToggleButtonGroup` usage** (grepped, confirmed) but an established, deliberate
124
- `RadioGroup` + `FormControlLabel` convention for single-choice controls
125
- (`src/messaging/PollCard.jsx:1-140`, code comment there explains why: native arrow-key nav + grouped
126
- screen-reader announcement — "a bare ungrouped `Radio` per option... loses both"). Prefer
127
- `RadioGroup`/`FormControlLabel` to match this repo's convention unless a segmented-button visual is
128
- judged clearly better for a 4-preset picker — either is acceptable, but state which and why.
129
-
130
- **Series toggles — `Checkbox` + `FormControlLabel` per series**, matching this repo's established
131
- multi-choice pattern (`PollCard.jsx`'s multi-choice mode; also `Composer.jsx:181`,
132
- `ProfileComponent.jsx:232,244`). **Accessibility requirement, verified as a real gap**: colors from
133
- `getNeutralChartPalette` are assigned positionally with no name-to-color map and no shape
134
- differentiation (`src/components/charts/palette.js`, full file) — so each checkbox's `label` MUST be
135
- the series' text name (`FormControlLabel` gives this for free), not a bare colored swatch. Optionally
136
- also render a small colored swatch next to the label (matching the series' `BarChart` color at the
137
- same index) as a secondary, non-exclusive cue.
138
-
139
- **Second y-axis — decision required by the WO, make it explicitly:** `BarChart` already supports a
140
- dual-axis shape (`yAxisKey: 'secondary'` per series, paired with `yAxis={[{id:'primary'},
141
- {id:'secondary'}]}`), so it is technically available — but **do not use it here**. Scope B's series
142
- toggles are the WO's chosen, required fix for the shared-axis-different-units problem (jg's user
143
- counts vs. presence-hours); a second y-axis is the *alternative* the WO explicitly did not choose.
144
- State this decision plainly in the completion note — do not leave it to be inferred from absence.
145
-
146
- **Empty state — all series deselected:** `ChartFrame`'s `isEmpty` prop drives its own empty-state
147
- `Alert` (`:58-65`). When every series is toggled off, `TimeSeriesChart` must NOT pass an empty
148
- `series` array to `BarChart` (which would likely throw or render a blank axis) — instead set
149
- `isEmpty` truthy on `ChartFrame` and skip rendering `BarChart` entirely for that render, matching
150
- required test 3 ("stable empty chart: no crash, no spinner").
151
-
152
- **i18n — extend `src/i18n/chartsTranslations.ts` in place** (single `chartsTranslations` object,
153
- `<ComponentName>.<CONSTANT_KEY>` convention, all four `de/fr/en/sw`, `:1-38` for the existing shape
154
- to match). New keys needed: `TimeSeriesChart.RANGE_1_DAY`, `RANGE_1_WEEK`, `RANGE_1_MONTH`,
155
- `RANGE_1_YEAR`, and a label for the series-toggle group (e.g. `TimeSeriesChart.SERIES_LABEL`).
156
-
157
- **Tests — new `tests/TimeSeriesChart.test.jsx`**, matching `tests/ChartFrame.test.jsx`/
158
- `tests/BarChart.test.jsx` conventions (both fully precedent): `// @vitest-environment jsdom` header,
159
- vitest + `@testing-library/react`, `afterEach(cleanup)`, a minimal per-file `i18next.createInstance()`
160
- + `I18nextProvider` (not the app's real i18n config), wrapped in `ThemeProvider theme={createTheme()}`.
161
- For test 1 (range emits granularity) and the series-filter tests, mock `@mui/x-charts/BarChart` itself
162
- via `vi.hoisted` + `vi.mock` to inspect exact props passed through (`BarChart.test.jsx`'s own pattern,
163
- `chartSpy.mock.calls[0][0]`) — do not mock ucm's own `BarChart`/`ChartFrame`, the point is testing the
164
- real composition.
165
-
166
- ## NON-GOALS / DO NOT TOUCH
167
- - Do not fetch data, and do not import anything app-specific.
168
- - Do not change `ChartFrame`, `BarChart`, `LineChart` or the palette beyond what composing requires. If
169
- `ChartFrame` lacks something, **extend it deliberately and say so** — do not work around it in the
170
- preset.
171
- - Do not add a charting dependency; MUI X-Charts is already the kit's basis.
172
- - No persistence of the picker or toggle state — that is the host's decision.
173
-
174
- ## RISKS
175
- - The toolbar slot has no consumer today, so this is its first real use. Expect to find gaps in it;
176
- fixing them is in scope, working around them is not.
177
- - Accessibility: the picker and checkboxes are controls, not decoration — labels, keyboard operation and
178
- state must be right. Series colour alone must not be the only way to tell which toggle maps to which
179
- series. `ui_reviewer` should check this specifically.
180
- - Every host inherits the preset; a visible behaviour change beyond jg.
181
-
182
- ## REQUIRED TESTS TO WRITE
183
- Narrow and behavioural. Do NOT run the full suite.
184
-
185
- 1. Selecting a range emits both the range **and** the mapped granularity to the callback — assert the
186
- granularity, which is the half most likely to be dropped.
187
- 2. Toggling a series off removes it from the rendered chart and leaves the others.
188
- 3. Toggling **all** series off renders a stable empty chart: no crash, no spinner.
189
- 4. The preset renders `ChartFrame`'s empty state for empty data, exactly once.
190
-
191
- **Non-vacuity:** test 1 must fail if only the range is emitted. Assert the emitted payload, not that a
192
- callback fired.
193
-
194
- ## TEST SCOPE FOR THE GATE (orchestrator)
195
- The chart component tests. Not the full suite.
196
-
197
- ## TARGET REPO
198
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main` (or `develop` if present). Publish per
199
- the repo's release flow; consumer pin bumps are separate.
200
-
201
- ## MINI-HANDOVER (pastable)
202
-
203
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`, or `develop` if present).
204
- > Work order: `work-orders/CHART-2.md` — read it fully, then follow the `orchestrate-codex` skill.
205
- >
206
- > Extends CHART-1, whose `ChartFrame` already has an unused **toolbar slot** — that is where the range
207
- > picker and series toggles go. **Presentational only: the host fetches, this component takes data and
208
- > callbacks.**
209
- >
210
- > The one thing most likely to be half-done: the range picker must emit a **granularity** alongside the
211
- > window. dcm ACT-1 aggregates server-side, and a 1-year range at 4-hour buckets is ~2190 points — a
212
- > picker that widens the window without coarsening the resolution reproduces the unreadable chart this
213
- > WO exists to fix. Test 1 asserts the granularity for that reason.
214
- >
215
- > The series toggles are a real fix, not a preference: jg's two series share one y-axis with different
216
- > units, which is why one of them is invisible today. If you consider a second y-axis instead, decide
217
- > deliberately and say so.
218
-
219
- ## PROGRESS CONTRACT
220
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
221
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
222
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
@@ -1,163 +0,0 @@
1
- # WORK ORDER CHART-3 (ui-core-micha) — fix TimeSeriesChart rendering nothing on async data load
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
-
10
- Bug-fix WO on the landed CHART-2. Found live in jg-ferien's "Aktivität" screen (ACT-3 consumer): the
11
- toolbar (range picker + series checkboxes) renders correctly, but the chart body never appears —
12
- `ChartFrame`'s empty-state `Alert` shows permanently, even once real data has loaded.
13
-
14
- ## TIER
15
- Tier 2 — shared-core UI consumed by every app that adopts `TimeSeriesChart`.
16
- Independent `reviewer` + `ui_reviewer` mandatory.
17
-
18
- ## WHY
19
-
20
- `TimeSeriesChart` initializes `visibleKeys` via a **lazy `useState` initializer** that reads
21
- `seriesConfig` (derived from the `data` prop) only once, at first mount:
22
-
23
- ```js
24
- const [visibleKeys, setVisibleKeys] = useState(
25
- () => new Set(seriesConfig.map((series) => series.key)),
26
- );
27
- ```
28
-
29
- jg-ferien's `ActivitySection.jsx` mounts `TimeSeriesChart` with `data = { xLabels: [], series: [] }`
30
- (its own initial `useState`), then fetches asynchronously in a `useEffect` and updates `data` only
31
- once the API call resolves — the standard "mount empty, fill later" pattern. Because the lazy
32
- initializer never re-runs on prop updates, `visibleKeys` stays a permanently empty `Set` even after
33
- real series arrive. Downstream:
34
-
35
- ```js
36
- const visibleSeries = seriesConfig.filter((series) => visibleKeys.has(series.key)); // always []
37
- const isDataEmpty = seriesConfig.length === 0 || visibleSeries.length === 0; // always true
38
- ```
39
-
40
- `isDataEmpty` is permanently `true`, so `ChartFrame` renders its empty-state `Alert` forever, no
41
- matter how much data loads. The toolbar renders fine because the range picker and the series
42
- checkboxes are driven directly off the live `seriesConfig`, not off `visibleKeys` — only the chart
43
- body is gated on the stale set. This reproduces on every host that fetches asynchronously after
44
- mount (the overwhelmingly common case), not just jg-ferien.
45
-
46
- ## SCOPE
47
-
48
- **Fix `visibleKeys` to track newly-appeared series without discarding a user's manual toggle.**
49
-
50
- Required behaviour:
51
- 1. When `data`/`seriesConfig` changes and introduces a series key not seen before, that key must
52
- become visible by default (matches current first-mount behaviour, just not mount-only).
53
- 2. A series key the user has manually toggled off must **stay off** across a range change that
54
- still includes that key (e.g. don't reset all toggles to "all visible" on every `data` update —
55
- that would silently undo the user's own toggle, a second bug of the opposite kind).
56
- 3. A key that disappears from `seriesConfig` (e.g. a range change that drops a series) and later
57
- reappears is treated as new for step 1 (simplest correct behaviour; do not attempt to remember
58
- toggle state for absent keys unless it falls out naturally from the fix).
59
-
60
- This is a "seen this key before, default it visible; otherwise respect the user's own toggle" sync,
61
- not a full rewrite of the toggle model — `toggleSeries`, the checkbox rendering, and the empty-state
62
- logic in SCOPE stay as they are; only the initialization/sync of `visibleKeys` changes.
63
-
64
- ## NON-GOALS / DO NOT TOUCH
65
- - Do not change `ChartFrame`, `BarChart`, `LineChart`, or the palette.
66
- - Do not change the range-picker or the range-to-granularity mapping (`RANGE_OPTIONS`).
67
- - Do not add an `onSeriesToggle` callback or any other new prop/API surface — this is a bug fix
68
- within the existing component contract, not a scope extension.
69
- - Do not touch `chartsTranslations.ts` — this WO is unrelated to i18n (a separate, already-diagnosed
70
- issue in a different repo is being fixed independently).
71
-
72
- ## RISKS
73
- - The fix must not reintroduce the mount-time bug in a different form (e.g. resetting on every
74
- render, or resetting on every `data` change including in-place mutations of the same series set —
75
- that would silently discard the user's manual toggles on every range change, which is the
76
- behaviour test 2 below exists to catch).
77
- - Every host that has adopted `TimeSeriesChart` since CHART-2 shipped is affected by this fix
78
- (currently only jg-ferien via ACT-3); this changes rendering behaviour for all of them, in the
79
- correct direction (chart now actually renders).
80
-
81
- ## REQUIRED TESTS TO WRITE
82
- Narrow and behavioural, extending `tests/TimeSeriesChart.test.jsx`. Do NOT run the full suite.
83
-
84
- 1. **Regression test for this exact bug**: mount `TimeSeriesChart` with empty `data`
85
- (`{ xLabels: [], series: [] }`), then rerender with populated `data` (real `xLabels`/`series`) —
86
- assert the chart body renders (not `ChartFrame`'s empty state), i.e. `isEmpty` becomes `false`
87
- and `BarChart` receives the new series. This must fail against the current code and pass after
88
- the fix — it is the non-vacuity check for this WO.
89
- 2. **Toggle persistence across a data update**: toggle one series off, then rerender with updated
90
- `data` that still contains that series key (e.g. a value change, same keys) — assert that series
91
- is still filtered out (the user's manual toggle was not reset by the data update).
92
- 3. **New series key appearing later defaults to visible**: mount with one series, rerender with a
93
- second series key added — assert the new key is visible by default while any prior manual toggle
94
- on the first key is preserved.
95
-
96
- **Non-vacuity:** test 1 must fail against the current `visibleKeys` lazy-initializer code (verify
97
- this before considering the fix complete) — it is reproducing the exact live bug from jg-ferien.
98
-
99
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
100
-
101
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
102
-
103
- **File to change: `src/components/charts/TimeSeriesChart.jsx`.**
104
-
105
- Current state (verified, full relevant slice):
106
- ```js
107
- const seriesConfig = data?.series || [];
108
-
109
- const [range, setRange] = useState(defaultRange);
110
- // Series identity (keys) is assumed stable for a given chart instance — the
111
- // toggle set is initialized once from the first data the chart receives.
112
- const [visibleKeys, setVisibleKeys] = useState(
113
- () => new Set(seriesConfig.map((series) => series.key)),
114
- );
115
- ```
116
- (`TimeSeriesChart.jsx:52-59`) — the comment above `visibleKeys` states the now-disproven assumption
117
- ("series identity is stable for a given chart instance") that this WO corrects; update or remove it
118
- to match the new behaviour.
119
-
120
- `toggleSeries` (`:68-75`), `visibleSeries`/`visiblePalette`/`isDataEmpty` derivation (`:77-84`), and
121
- the checkbox rendering (`:103-123`) are all downstream of `visibleKeys` and should not need changes
122
- — only how `visibleKeys` is initialized/kept in sync with `seriesConfig` changes.
123
-
124
- **Suggested shape (not prescriptive — implement whatever satisfies SCOPE correctly):** replace the
125
- lazy `useState` with a `useEffect` keyed on the set of series keys in `seriesConfig`, that adds any
126
- newly-seen key to `visibleKeys` (defaulting it visible) without touching keys already present in the
127
- set — e.g. track previously-seen keys in a ref to detect "new" vs "toggled off", since a key
128
- present in both old and new `seriesConfig` but absent from `visibleKeys` must stay absent (it was a
129
- deliberate user toggle-off, not a fresh key). Keep `visibleKeys`'s existing `Set`-of-keys shape,
130
- `toggleSeries`'s update pattern, and the checkbox/empty-state code untouched.
131
-
132
- **Test file to extend: `tests/TimeSeriesChart.test.jsx`** — matches `tests/ChartFrame.test.jsx`/
133
- `tests/BarChart.test.jsx` conventions: `// @vitest-environment jsdom` header, vitest +
134
- `@testing-library/react`, `afterEach(cleanup)`, a minimal per-file `i18next.createInstance()` +
135
- `I18nextProvider`, wrapped in `ThemeProvider theme={createTheme()}`. Use `rerender()` from
136
- `@testing-library/react`'s `render()` return value to simulate the host updating `data` after mount
137
- (this is the exact scenario CHART-2's own tests never covered — they only mounted once with
138
- synchronous populated data).
139
-
140
- ## TARGET REPO
141
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release
142
- flow (patch bump — bug fix, no interface change); consumer pin bumps are separate, out of scope.
143
-
144
- ## TEST SCOPE FOR THE GATE (orchestrator)
145
- `tests/TimeSeriesChart.test.jsx` only. Not the full suite.
146
-
147
- ## MINI-HANDOVER (pastable)
148
-
149
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`).
150
- > Work order: `work-orders/CHART-3.md` — read it fully, then follow the `orchestrate-codex` skill.
151
- >
152
- > Bug fix on CHART-2's `TimeSeriesChart`: `visibleKeys` is initialized once via a lazy `useState`
153
- > from whatever `data` is at first mount, so any host that fetches asynchronously after mount (the
154
- > normal case) ends up with a permanently-empty `visibleKeys` and the chart body never renders —
155
- > `ChartFrame`'s empty state shows forever even once real data arrives. Fix the sync so newly-seen
156
- > series keys default to visible while a user's manual toggle-off on an already-seen key survives a
157
- > data update. Regression test 1 is the non-vacuity check — verify it fails against current code
158
- > first.
159
-
160
- ## PROGRESS CONTRACT
161
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
162
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
163
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
@@ -1,203 +0,0 @@
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>`.