@micha.bigler/ui-core-micha 3.14.4 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +29 -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 +31 -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,226 +0,0 @@
1
- # WORK ORDER CHART-5 (ui-core-micha) — dual y-axis + always-visible x-axis ticks
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
- Feature WO on `TimeSeriesChart` (CHART-2/3/4). Operator request, found live in jg-ferien's Aktivität
13
- screen: (a) the shared y-axis mixes an integer count series ("Eindeutige Nutzer") with a fractional
14
- hours series ("Anwesenheitszeit"), producing decimal ticks (0.2, 0.4, ...) that don't make sense for a
15
- user count; (b) the x-axis (time buckets) shows **zero** visible tick labels once there are enough
16
- buckets that MUI X-Charts' built-in collision-avoidance decides none of them fit — worse than showing
17
- a sparse subset, this shows nothing at all.
18
-
19
- ## TIER
20
- Tier 2 — shared-core UI, new public surface (a new optional per-series field). Independent `reviewer`
21
- + `ui_reviewer` mandatory.
22
-
23
- ## WHY
24
-
25
- **Y-axis.** CHART-2 deliberately rejected a second y-axis in favor of series toggles ("if you do not
26
- add one, say so deliberately" — CHART-2 said so, choosing toggles). The operator has now explicitly
27
- requested the second y-axis for this exact case, with full awareness of that prior decision (asked
28
- in-chat, chose it knowingly over forcing shared-axis integer ticks — which would make the fractional
29
- series nearly invisible — and over hiding the tick label only when one series is toggled off). This
30
- WO reverses CHART-2's choice on operator authority, it does not silently second-guess it.
31
-
32
- **X-axis.** `node_modules/@mui/x-charts/ChartsXAxis/getVisibleLabels.js`'s default behaviour drops a
33
- tick label the moment it would overlap the previous one (`tickLabelMinGap`) — with many narrow-band
34
- categories (e.g. 24 hourly buckets) and non-trivial label text (jg-ferien's `formatBucketLabel`
35
- produces e.g. `"08/06, 08:00"`), every label can end up colliding and the *entire* axis goes blank.
36
- MUI's own escape hatch is `tickLabelInterval` as a **function** — when provided, it fully replaces the
37
- collision-based filtering (`getVisibleLabels.js`: `if (typeof tickLabelInterval === 'function') return
38
- new Set(xTicks.filter((item, index) => tickLabelInterval(item.value, index)))`), so a deterministic,
39
- evenly-spaced selection can guarantee *some* labels always render regardless of container width or
40
- label length.
41
-
42
- ## SCOPE
43
-
44
- **A. Per-series `axis` field, backward-compatible.** Each entry in `data.series[i]` gains an optional
45
- `axis: 'primary' | 'secondary'` field. Default (omitted, or every series `'primary'`): **byte-for-byte
46
- today's single-shared-axis behaviour** — no `yAxis` array change, no `yAxisKey` on series, nothing
47
- observably different for existing consumers (jg-ferien pre-adoption, or any future host that doesn't
48
- use the field). Only when **at least one** series declares `axis: 'secondary'` does dual-axis mode
49
- activate:
50
- - Build `yAxis` as two entries: primary (`id: 'primary'`, label = existing `yAxisLabel` prop) and
51
- secondary (`id: 'secondary'`, label = new prop `secondaryYAxisLabel`).
52
- - Each series passed to `BarChart` gets `yAxisKey: 'secondary'` when its `axis` is `'secondary'`,
53
- omitted (defaults to the first/primary axis) otherwise — `series` items already pass straight
54
- through `BarChart`'s `...chartProps` to the real `MuiBarChart`, which natively supports
55
- `yAxisKey` per series; no `BarChart.jsx` change needed for this part, verify that during
56
- implementation rather than assuming.
57
- - `secondaryYAxisLabel` is required (throw, matching `BarChart`'s own `xAxisLabel`/`yAxisLabel`
58
- required-prop convention) **only when dual-axis mode is actually active** (i.e. when computing
59
- it: if any series has `axis === 'secondary'` and `secondaryYAxisLabel` is falsy, throw with a
60
- clear message) — do not require it unconditionally, that would break every existing single-axis
61
- caller.
62
-
63
- **B. Auto-detected integer-only ticks per axis group, not a new prop.** For each y-axis actually in
64
- use (primary always; secondary only in dual-axis mode), if **every** data value across that axis's
65
- currently-**visible** series (respecting the existing series-toggle `visibleKeys` state — an axis
66
- whose only integer series just got toggled off should not keep forcing integer ticks on whatever
67
- remains) is a whole number (`Number.isInteger`), apply a `valueFormatter` to that axis's config that
68
- only emits a label for integer tick values and an empty string otherwise (mirrors the X-axis
69
- collision convention: `getVisibleLabels.js` already treats `formattedValue === ''` as "don't show
70
- this tick" for the X axis; verify the Y-axis tick renderer honours the same convention during
71
- implementation — if it does not, use whatever mechanism it actually supports to the same effect, and
72
- say so in the completion note). If not all-integer, leave the axis's default (no custom formatter) —
73
- this is what keeps `Anwesenheitszeit` (fractional hours) unaffected on its own axis, and correctly
74
- does nothing extra for a primary axis that legitimately has fractional data.
75
-
76
- **C. Deterministic x-axis tick visibility.** Replace MUI's default collision-based tick filtering with
77
- an explicit `tickLabelInterval` function on the x-axis config, evenly sampling up to `MAX_X_TICKS`
78
- (pick a sensible constant, e.g. 8) labels across `data.xLabels`, e.g. `index % Math.max(1,
79
- Math.ceil(xLabels.length / MAX_X_TICKS)) === 0` (adjust as needed so the *last* category is also
80
- reachable/considered, not just an artifact of the modulo — check this doesn't systematically drop the
81
- final bucket). This must apply regardless of dual-axis mode (independent scope).
82
-
83
- ## NON-GOALS / DO NOT TOUCH
84
- - No third axis, no per-axis custom colors, no new axis-styling props beyond what SCOPE describes.
85
- - Do not touch the range picker, series-toggle checkboxes' own rendering, `chartsTranslations`, or the
86
- CHART-3/CHART-4 fixes (`visibleKeys` sync, `CHART_HEIGHT`).
87
- - Do not change `BarChart.jsx`/`ChartFrame.jsx` unless SCOPE A's `yAxisKey` passthrough turns out to
88
- need it (verify first, per SCOPE A's own note) — if it does, extend deliberately and say so, matching
89
- CHART-2's own "do not work around a primitive gap in the preset" rule.
90
- - jg-ferien's own adoption (setting `axis: 'secondary'` on `presence_hours`, adding a
91
- `secondaryYAxisLabel`, shortening `formatBucketLabel`) is **explicitly out of scope for this WO** —
92
- a separate companion change in that repo, after this publishes.
93
-
94
- ## RISKS
95
- - `tickLabelInterval` as a function fully bypasses MUI's built-in overlap detection (per SCOPE C's
96
- quoted source) — a fixed `MAX_X_TICKS` could still produce overlapping text at extreme narrow
97
- widths with very long labels. Accepted: "some labels, deterministically" is strictly better than
98
- "zero labels, unpredictably" (today's bug), and host-side label shortening (jg-ferien's future
99
- companion change) is the complementary fix for extreme cases, not this WO's job.
100
- - Every host that adopts `axis: 'secondary'` changes its chart's visual scale contract (two
101
- independent y-domains can visually mislead if not clearly labelled) — mitigated by requiring
102
- `secondaryYAxisLabel`, not by this WO deciding it's always fine.
103
- - The integer-tick auto-detection (SCOPE B) re-evaluates on every toggle (depends on `visibleKeys`) —
104
- confirm this doesn't cause a visible axis-label flicker/jump on toggle beyond the intended "ticks
105
- change because domain changed" behaviour the toggles already produce today.
106
-
107
- ## REQUIRED TESTS TO WRITE
108
- Extend `tests/TimeSeriesChart.test.jsx`. Do NOT run the full suite.
109
-
110
- **Learn from CHART-4's own history in this file (read `WORK_ORDERS.md`'s CHART-4 row before writing
111
- these): an unmocked test asserting real rendered DOM/tick visibility was proven VACUOUS in this exact
112
- component (jsdom's layout/ResizeObserver handling doesn't reproduce real-browser collision behaviour).
113
- Do not repeat that mistake here.** For anything about tick *visibility* or *positions* (SCOPE C, and
114
- SCOPE B's empty-string-for-non-integer-ticks mechanism), assert on the **function/config actually
115
- passed** to the mocked `MuiBarChart` (`chartSpy.mock.calls`) — e.g. call the captured
116
- `tickLabelInterval` function directly with sample `(value, index)` pairs and assert its boolean
117
- results, or call the captured y-axis `valueFormatter` directly with sample tick values and assert its
118
- string output — never assert on rendered pixel positions or DOM overlap in jsdom.
119
-
120
- 1. **Dual-axis backward compatibility**: with no series declaring `axis`, `chartSpy`'s `yAxis` prop
121
- must be unchanged from today's single-entry shape (or however it's currently constructed — assert
122
- equivalently to the existing tests, not a new shape).
123
- 2. **Dual-axis activation**: with one series `axis: 'secondary'`, assert `chartSpy`'s `yAxis` has two
124
- entries (primary/secondary ids, correct labels from `yAxisLabel`/`secondaryYAxisLabel`), and assert
125
- the secondary series' entry in `chartSpy`'s `series` prop carries `yAxisKey` matching the secondary
126
- axis id while the other series' entry does not (or carries the primary id — whichever the
127
- implementation settles on, be explicit and consistent).
128
- 3. **Missing `secondaryYAxisLabel` throws** when a series declares `axis: 'secondary'` but the prop is
129
- absent — mirrors `BarChart`'s own required-prop throw test convention (check
130
- `tests/BarChart.test.jsx` for that pattern).
131
- 4. **Integer-tick auto-detection, both directions**: one case where a y-axis's visible series are all
132
- whole numbers (assert the captured `valueFormatter` returns a non-empty string for an integer tick
133
- value and `''` for a non-integer one), and one case where they are not all whole numbers (assert no
134
- custom `valueFormatter` is applied, or that it returns the value formatted normally for every tick —
135
- whichever the implementation does, assert it explicitly).
136
- 5. **Toggle re-evaluation**: toggling off the one non-integer series on a shared/primary axis (single-
137
- axis mode) changes that axis from "not all-integer" to "all-integer" — assert the formatter
138
- behaviour changes accordingly after the toggle. (This is the SCOPE B "re-evaluates on toggle" case,
139
- RISKS section.)
140
- 6. **X-axis `tickLabelInterval` guarantees coverage**: call the captured function against a large
141
- `xLabels` array (e.g. 24 entries) and assert it returns `true` for at least `N` evenly-spread
142
- indices (pick a concrete, checkable lower bound, e.g. at least 3 for 24 items at `MAX_X_TICKS=8`)
143
- and that it does not return `true` for literally zero indices (the non-vacuity check for the whole
144
- WO — the very bug this section fixes).
145
- 7. **Non-vacuity**: each new test must fail against the current (pre-fix) code — verify before
146
- considering the fix complete. (SCOPE C's test 6 obviously fails today since there is no
147
- `tickLabelInterval` at all; confirm the others similarly.)
148
-
149
- ## TARGET REPO
150
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release flow
151
- (minor bump — new optional field/prop, additive, no breaking change to existing callers).
152
-
153
- ## TEST SCOPE FOR THE GATE (orchestrator)
154
- `tests/TimeSeriesChart.test.jsx`. Not the full suite.
155
-
156
- ## CONTEXT PACKAGE — verified current state (Orchestrator, Implementation map)
157
-
158
- Work from this package; do not explore broadly from scratch — open only the named files to verify.
159
-
160
- **File to change: `src/components/charts/TimeSeriesChart.jsx`** — current full return statement and
161
- surrounding derived-state variables (`:117-189`, verified current):
162
- ```jsx
163
- const visibleSeries = seriesConfig.filter((series) => visibleKeys.has(series.key));
164
- const visiblePalette = visibleSeries.map((series) => palette[seriesConfig.indexOf(series) % palette.length]);
165
- const isDataEmpty = seriesConfig.length === 0 || visibleSeries.length === 0;
166
- ...
167
- return (
168
- <ChartFrame ... minHeight={CHART_HEIGHT}>
169
- {!isDataEmpty && (
170
- <BarChart
171
- xAxisLabel={xAxisLabel}
172
- yAxisLabel={yAxisLabel}
173
- xAxis={[{ data: data?.xLabels || [] }]}
174
- series={visibleSeries.map((series) => ({ data: series.data, label: series.label }))}
175
- palette={visiblePalette}
176
- height={CHART_HEIGHT}
177
- />
178
- )}
179
- </ChartFrame>
180
- );
181
- ```
182
- Component signature currently: `{ title, subtitle, xAxisLabel, yAxisLabel, data, loading, error,
183
- onRangeChange, defaultRange }` (`:56-66`) — add `secondaryYAxisLabel` here.
184
-
185
- **`BarChart.jsx`** (full file, unchanged by this WO unless SCOPE A's verification says otherwise):
186
- `series`/`xAxis`/`yAxis` are BarChart's own named props; `series` is passed straight through
187
- (`series={series}` onto `MuiBarChart`, `:35`) with NO per-item transformation — so a `yAxisKey` field
188
- already present on a series object the caller builds should reach `MuiBarChart` unmodified. `yAxis`
189
- goes through `labelledAxis(yAxis, yAxisLabel, {scaleType:'linear'})` (`:37`), which preserves any
190
- `label` already set on an axis entry (only fills in the default when absent) — so `TimeSeriesChart`
191
- passing two fully-labelled `yAxis` entries will not have `BarChart`'s own `yAxisLabel` prop override
192
- them; **`TimeSeriesChart` should stop passing a single top-level `yAxisLabel` to `BarChart` once it is
193
- building its own multi-entry `yAxis` array explicitly** (or `BarChart`'s `yAxisLabel` requirement
194
- throws — check `BarChart.jsx:27-29`'s `if (!xAxisLabel || !yAxisLabel) throw` and account for it:
195
- either keep passing `yAxisLabel` as before, since `labelledAxis` won't clobber pre-labelled entries
196
- anyway, or restructure — pick whichever keeps `BarChart`'s existing required-prop contract intact
197
- without a special case).
198
-
199
- **X-axis**: `xAxis={[{ data: data?.xLabels || [] }]}` (`:181` current) — add `tickLabelInterval` to
200
- this object; `labelledAxis` will preserve it (only fills `label`, spreads everything else through).
201
-
202
- **Existing tests to model conventions on**: `tests/TimeSeriesChart.test.jsx` (chartSpy mock pattern,
203
- already described above) and `tests/BarChart.test.jsx` (required-prop-throws test pattern for the
204
- `secondaryYAxisLabel` throw case).
205
-
206
- ## MINI-HANDOVER (pastable)
207
-
208
- > Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`).
209
- > Work order: `work-orders/CHART-5.md` — read it fully, then follow the `orchestrate-codex` skill.
210
- > **Codex workspace is out of credits this session — go straight to the direct-Claude fallback.**
211
- >
212
- > Two independent additions to `TimeSeriesChart`, both operator-requested after seeing the shipped
213
- > CHART-2/3/4 chart live: (1) an opt-in second y-axis via a new per-series `axis: 'secondary'` field,
214
- > fully backward-compatible when unused, with auto-detected integer-only tick labels per axis group
215
- > (not a new prop — derived from whether that axis's visible series data is all whole numbers); (2) a
216
- > deterministic x-axis `tickLabelInterval` so time-bucket labels never all vanish under MUI's default
217
- > collision-avoidance, which is exactly what happened live (zero labels shown with 24 hourly buckets).
218
- > **Test-writing pitfall already hit once in this file (CHART-4): do not write a test that asserts on
219
- > real jsdom-rendered tick visibility/position — it will be vacuous. Assert on the captured
220
- > `tickLabelInterval`/`valueFormatter` functions' own return values instead**, calling them directly
221
- > with sample inputs against the mocked `chartSpy` call's props.
222
-
223
- ## PROGRESS CONTRACT
224
- Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
225
- relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
226
- stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.