@micha.bigler/ui-core-micha 3.14.3 → 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 (367) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +7 -0
  3. package/dist/auth/UserMenu.js +1 -1
  4. package/dist/auth/apiClient.js +7 -6
  5. package/dist/chat/ChatList.js +1 -1
  6. package/dist/chat/ChatThread.js +2 -1
  7. package/dist/chat/ChatWorkspace.js +1 -1
  8. package/dist/chat/ModelPicker.js +2 -5
  9. package/dist/chat/kitSxRegistry.js +9 -0
  10. package/dist/chat/useChatSocket.js +1 -1
  11. package/dist/components/LoginForm.js +1 -1
  12. package/dist/components/SecurityComponent.js +1 -1
  13. package/dist/components/UserListComponent.js +2 -8
  14. package/dist/components/charts/ChartFrame.js +1 -1
  15. package/dist/components/charts/ScatterChart.js +2 -1
  16. package/dist/components/charts/kitSxRegistry.js +6 -0
  17. package/dist/entries/charts.js +6 -0
  18. package/dist/entries/chat.js +8 -0
  19. package/dist/entries/markdown.js +1 -0
  20. package/dist/index.js +0 -14
  21. package/dist/layout/SectionNav.js +1 -1
  22. package/dist/messaging/AttachmentList.js +2 -2
  23. package/dist/messaging/Composer.js +2 -1
  24. package/dist/messaging/ConversationLaunchers.js +1 -4
  25. package/dist/messaging/ConversationList.js +1 -1
  26. package/dist/messaging/DirectMessageLauncher.js +1 -1
  27. package/dist/messaging/MessagingProvider.js +0 -0
  28. package/dist/messaging/PollCard.js +7 -5
  29. package/dist/messaging/ReadTicks.js +2 -2
  30. package/dist/messaging/Thread.js +5 -3
  31. package/dist/notifications/NotificationBell.js +4 -7
  32. package/dist/notifications/NotificationSettings.js +1 -1
  33. package/dist/onboarding/OnboardingProvider.js +3 -6
  34. package/dist/pages/AccountPage.js +1 -2
  35. package/dist/pages/LoginPage.js +2 -3
  36. package/dist/pages/PasswordChangePage.js +2 -3
  37. package/dist/pages/PasswordInvitePage.js +2 -3
  38. package/dist/pages/PasswordResetRequestPage.js +2 -3
  39. package/dist/pages/SignUpPage.js +2 -3
  40. package/dist/pages/SignupConfirmPage.js +2 -3
  41. package/dist/theme/createAppTheme.js +5 -4
  42. package/dist/theme/kitSxEntry.js +11 -0
  43. package/dist/theme/kitSxRegistry.js +2 -15
  44. package/dist/theme/themeCompleteness.js +3 -12
  45. package/dist/utils/auth-errors.js +4 -3
  46. package/package.json +41 -16
  47. package/.github/workflows/publish.yml +0 -126
  48. package/DESIGN.md +0 -60
  49. package/WORK_ORDERS.md +0 -148
  50. package/dev/entries.jsx +0 -721
  51. package/dev/fixtures.js +0 -26
  52. package/dev/main.jsx +0 -75
  53. package/dev/mockTransport.jsx +0 -128
  54. package/docs/CHART-LAYOUT.md +0 -157
  55. package/docs/DARK-TOKENS.md +0 -140
  56. package/docs/TYPE-SCALE.md +0 -111
  57. package/docs/messaging-deviations.md +0 -118
  58. package/index.html +0 -12
  59. package/renovate.json +0 -10
  60. package/scripts/chart-api-census.mjs +0 -415
  61. package/src/auth/AuthContext.jsx +0 -158
  62. package/src/auth/ReauthModal.jsx +0 -82
  63. package/src/auth/UserMenu.jsx +0 -121
  64. package/src/auth/apiClient.jsx +0 -232
  65. package/src/auth/authApi.jsx +0 -539
  66. package/src/auth/authConfig.jsx +0 -34
  67. package/src/auth/reauth.js +0 -47
  68. package/src/chat/ChatComposer.jsx +0 -36
  69. package/src/chat/ChatList.jsx +0 -87
  70. package/src/chat/ChatThread.jsx +0 -128
  71. package/src/chat/ChatWorkspace.jsx +0 -244
  72. package/src/chat/MarkdownReply.jsx +0 -22
  73. package/src/chat/ModelPicker.jsx +0 -85
  74. package/src/chat/api.js +0 -18
  75. package/src/chat/useChatSocket.js +0 -32
  76. package/src/chat/wire.js +0 -7
  77. package/src/components/AccessCodeManager.jsx +0 -288
  78. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  79. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  80. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  81. package/src/components/BulkInviteCsvTab.jsx +0 -198
  82. package/src/components/EmptyState.jsx +0 -38
  83. package/src/components/LoginForm.jsx +0 -159
  84. package/src/components/MFAComponent.jsx +0 -350
  85. package/src/components/MfaLoginComponent.jsx +0 -252
  86. package/src/components/MobileBottomNav.jsx +0 -82
  87. package/src/components/PasskeysComponent.jsx +0 -252
  88. package/src/components/PasswordChangeForm.jsx +0 -87
  89. package/src/components/PasswordResetRequestForm.jsx +0 -43
  90. package/src/components/PasswordSetForm.jsx +0 -97
  91. package/src/components/ProfileComponent.jsx +0 -348
  92. package/src/components/QrSignupManager.jsx +0 -404
  93. package/src/components/QrSignupValidityManager.jsx +0 -106
  94. package/src/components/RegistrationMethodsManager.jsx +0 -150
  95. package/src/components/SecurityComponent.jsx +0 -141
  96. package/src/components/SocialLoginButtons.jsx +0 -83
  97. package/src/components/SoftChip.jsx +0 -93
  98. package/src/components/StatTile.jsx +0 -39
  99. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  100. package/src/components/TurnstileWidget.jsx +0 -99
  101. package/src/components/UserInviteComponent.jsx +0 -76
  102. package/src/components/UserListComponent.jsx +0 -618
  103. package/src/components/charts/BarChart.jsx +0 -102
  104. package/src/components/charts/ChartFrame.jsx +0 -214
  105. package/src/components/charts/LineChart.jsx +0 -126
  106. package/src/components/charts/ScatterChart.jsx +0 -299
  107. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  108. package/src/components/charts/chartDefaults.js +0 -644
  109. package/src/components/charts/chartLabels.js +0 -36
  110. package/src/components/charts/exportChart.js +0 -258
  111. package/src/components/charts/formatters.js +0 -39
  112. package/src/components/charts/palette.js +0 -33
  113. package/src/components/charts/yearTickInterval.js +0 -7
  114. package/src/i18n/authTranslations.ts +0 -2651
  115. package/src/i18n/chartsTranslations.ts +0 -91
  116. package/src/i18n/chatTranslations.ts +0 -55
  117. package/src/i18n/messagingTranslations.ts +0 -120
  118. package/src/i18n/notificationsTranslations.ts +0 -32
  119. package/src/i18n/onboardingTranslations.ts +0 -29
  120. package/src/i18n/sectionNavTranslations.ts +0 -20
  121. package/src/i18n/uiCoreTranslations.js +0 -29
  122. package/src/i18n/userMenuTranslations.ts +0 -5
  123. package/src/index.js +0 -158
  124. package/src/layout/PageLayout.jsx +0 -36
  125. package/src/layout/SectionNav.jsx +0 -320
  126. package/src/messaging/AttachmentList.jsx +0 -145
  127. package/src/messaging/Composer.jsx +0 -197
  128. package/src/messaging/ConversationLaunchers.jsx +0 -51
  129. package/src/messaging/ConversationList.jsx +0 -109
  130. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  131. package/src/messaging/MessageBubble.jsx +0 -183
  132. package/src/messaging/MessagingProvider.jsx +0 -0
  133. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  134. package/src/messaging/PollCard.jsx +0 -163
  135. package/src/messaging/ReactionBar.jsx +0 -43
  136. package/src/messaging/ReadTicks.jsx +0 -82
  137. package/src/messaging/Thread.jsx +0 -195
  138. package/src/messaging/api.js +0 -48
  139. package/src/messaging/composerImageCompression.js +0 -35
  140. package/src/messaging/linkifyText.jsx +0 -37
  141. package/src/notifications/NotificationBell.jsx +0 -84
  142. package/src/notifications/NotificationSettings.jsx +0 -330
  143. package/src/notifications/NotificationsProvider.jsx +0 -224
  144. package/src/notifications/PopupSurface.jsx +0 -105
  145. package/src/notifications/api.js +0 -55
  146. package/src/notifications/feedApi.js +0 -18
  147. package/src/notifications/realtime.jsx +0 -138
  148. package/src/notifications/serviceWorker/sw.js +0 -50
  149. package/src/onboarding/OnboardingProvider.jsx +0 -326
  150. package/src/onboarding/OnboardingWizard.jsx +0 -61
  151. package/src/onboarding/WizardDialogShell.jsx +0 -51
  152. package/src/onboarding/api.js +0 -13
  153. package/src/onboarding/stepSelection.js +0 -16
  154. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  155. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  156. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  157. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  158. package/src/pages/AccountPage.jsx +0 -394
  159. package/src/pages/LoginPage.jsx +0 -343
  160. package/src/pages/PasswordChangePage.jsx +0 -62
  161. package/src/pages/PasswordInvitePage.jsx +0 -177
  162. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  163. package/src/pages/SignUpPage.jsx +0 -268
  164. package/src/pages/SignupConfirmPage.jsx +0 -155
  165. package/src/testing/checkKitIntegration.js +0 -233
  166. package/src/theme/createAppTheme.js +0 -423
  167. package/src/theme/fonts.js +0 -3
  168. package/src/theme/index.js +0 -21
  169. package/src/theme/kitSxRegistry.js +0 -285
  170. package/src/theme/themeCompleteness.js +0 -1045
  171. package/src/theme/tokens.js +0 -478
  172. package/src/utils/auth-errors.js +0 -76
  173. package/src/utils/authService.js +0 -223
  174. package/src/utils/resolveErrorText.js +0 -28
  175. package/src/utils/webauthn.js +0 -51
  176. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  177. package/tests/AccountPage.test.jsx +0 -200
  178. package/tests/AuthContext.test.jsx +0 -129
  179. package/tests/BarChart.test.jsx +0 -112
  180. package/tests/BrowserPushStep.test.jsx +0 -110
  181. package/tests/ChartFrame.test.jsx +0 -336
  182. package/tests/ConversationList.test.jsx +0 -144
  183. package/tests/CookieConsentStep.test.jsx +0 -69
  184. package/tests/EmptyState.test.jsx +0 -62
  185. package/tests/LineChart.test.jsx +0 -80
  186. package/tests/MessagingProvider.test.jsx +0 -50
  187. package/tests/MobileBottomNav.test.jsx +0 -319
  188. package/tests/NotificationBell.test.jsx +0 -60
  189. package/tests/NotificationSettings.test.jsx +0 -276
  190. package/tests/NotificationsProvider.test.jsx +0 -240
  191. package/tests/OnboardingProvider.test.jsx +0 -294
  192. package/tests/OnboardingWizard.test.jsx +0 -143
  193. package/tests/PasswordChangeForm.test.jsx +0 -70
  194. package/tests/PasswordInvitePage.test.jsx +0 -142
  195. package/tests/PasswordSetForm.test.jsx +0 -70
  196. package/tests/PopupSurface.test.jsx +0 -142
  197. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  198. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  199. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  200. package/tests/ScatterChart.test.jsx +0 -270
  201. package/tests/SectionNav.test.jsx +0 -363
  202. package/tests/SignUpPage.test.jsx +0 -208
  203. package/tests/SoftChip.test.jsx +0 -105
  204. package/tests/StatTile.test.jsx +0 -57
  205. package/tests/Thread.test.jsx +0 -151
  206. package/tests/TimeSeriesChart.test.jsx +0 -367
  207. package/tests/UserListComponent.test.jsx +0 -192
  208. package/tests/UserMenu.test.jsx +0 -171
  209. package/tests/apiClientLanguage.test.js +0 -33
  210. package/tests/apiClientPublicPaths.test.js +0 -178
  211. package/tests/authApiLogin.test.js +0 -42
  212. package/tests/authApiRegistration.test.js +0 -31
  213. package/tests/authErrorCodeRendering.test.jsx +0 -77
  214. package/tests/authErrors.test.js +0 -74
  215. package/tests/authTranslations.test.js +0 -74
  216. package/tests/chart-api-census.test.js +0 -366
  217. package/tests/chartAxisTickBand.test.jsx +0 -95
  218. package/tests/chartDefaults.test.js +0 -902
  219. package/tests/chartLabels.test.js +0 -50
  220. package/tests/chartsPalette.test.js +0 -99
  221. package/tests/chatApi.test.js +0 -48
  222. package/tests/chatDriverNames.test.jsx +0 -71
  223. package/tests/chatExports.test.js +0 -3
  224. package/tests/chatList.test.jsx +0 -84
  225. package/tests/chatPicker.test.jsx +0 -82
  226. package/tests/chatSocket.test.jsx +0 -26
  227. package/tests/chatThread.test.jsx +0 -116
  228. package/tests/chatTranslations.test.js +0 -47
  229. package/tests/chatWorkspace.test.jsx +0 -234
  230. package/tests/checkKitIntegration.test.jsx +0 -219
  231. package/tests/createAppTheme.test.js +0 -132
  232. package/tests/createAppThemeDark.test.js +0 -127
  233. package/tests/createAppThemeLightGuard.test.js +0 -40
  234. package/tests/createAppThemePageContrast.test.js +0 -108
  235. package/tests/createAppThemeTypography.test.js +0 -53
  236. package/tests/errorCallSiteGuard.test.js +0 -32
  237. package/tests/exportChart.test.js +0 -313
  238. package/tests/feedApi.test.js +0 -46
  239. package/tests/fixtures/light-theme-baseline.json +0 -2234
  240. package/tests/i18nAggregate.test.js +0 -120
  241. package/tests/i18nGermanVariant.test.js +0 -65
  242. package/tests/loginPageErrorResolution.test.jsx +0 -102
  243. package/tests/messagingApi.test.js +0 -32
  244. package/tests/messagingChunk6.test.jsx +0 -116
  245. package/tests/messagingCompactBubble.test.jsx +0 -64
  246. package/tests/messagingComposer.test.jsx +0 -136
  247. package/tests/messagingComposerChunk5.test.jsx +0 -113
  248. package/tests/messagingContractConformance.test.js +0 -69
  249. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  250. package/tests/messagingExports.test.js +0 -21
  251. package/tests/messagingInteractions.test.jsx +0 -136
  252. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  253. package/tests/messagingLinkify.test.jsx +0 -51
  254. package/tests/messagingMessageActions.test.jsx +0 -163
  255. package/tests/messagingMsg6.test.jsx +0 -105
  256. package/tests/messagingMsg6f.test.jsx +0 -155
  257. package/tests/messagingMsg6g.test.jsx +0 -166
  258. package/tests/messagingMsg6h.test.jsx +0 -136
  259. package/tests/messagingMsg6i.test.jsx +0 -129
  260. package/tests/messagingPollRendering.test.jsx +0 -142
  261. package/tests/messagingReactionMerge.test.jsx +0 -77
  262. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  263. package/tests/messagingThreadReceipts.test.jsx +0 -178
  264. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  265. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  266. package/tests/notificationsApi.test.js +0 -41
  267. package/tests/notificationsExports.test.js +0 -34
  268. package/tests/onboardingDescriptors.test.js +0 -132
  269. package/tests/packageTreeShaking.test.js +0 -126
  270. package/tests/realtime.test.jsx +0 -219
  271. package/tests/resolveErrorText.test.js +0 -72
  272. package/tests/stepSelection.test.js +0 -53
  273. package/tests/themeCompleteness.test.js +0 -626
  274. package/tests/timeSeriesLegend.test.js +0 -99
  275. package/tests/userScreenTranslations.test.jsx +0 -218
  276. package/tsconfig.build.json +0 -18
  277. package/vite.config.mjs +0 -6
  278. package/work-orders/AUTH-1.md +0 -74
  279. package/work-orders/AUTH-2.md +0 -74
  280. package/work-orders/AUTH-4.md +0 -183
  281. package/work-orders/AUTH-5.md +0 -154
  282. package/work-orders/AUTH-7.md +0 -181
  283. package/work-orders/AUTH-8.md +0 -184
  284. package/work-orders/CHART-1.md +0 -285
  285. package/work-orders/CHART-2.md +0 -222
  286. package/work-orders/CHART-3.md +0 -163
  287. package/work-orders/CHART-4.md +0 -203
  288. package/work-orders/CHART-5.md +0 -226
  289. package/work-orders/CHART-6.md +0 -428
  290. package/work-orders/CHART-7.md +0 -175
  291. package/work-orders/DX-1.md +0 -169
  292. package/work-orders/DX-2.md +0 -188
  293. package/work-orders/DX-3.md +0 -195
  294. package/work-orders/I18N-1.md +0 -254
  295. package/work-orders/I18N-3.md +0 -207
  296. package/work-orders/MSG-3.md +0 -375
  297. package/work-orders/MSG-3b.md +0 -1002
  298. package/work-orders/MSG-3c.md +0 -378
  299. package/work-orders/MSG-3d.md +0 -268
  300. package/work-orders/MSG-3e.md +0 -164
  301. package/work-orders/MSG-3f.md +0 -391
  302. package/work-orders/MSG-6e.md +0 -308
  303. package/work-orders/MSG-6f.md +0 -214
  304. package/work-orders/MSG-6g.md +0 -244
  305. package/work-orders/MSG-6h.md +0 -238
  306. package/work-orders/MSG-6i.md +0 -271
  307. package/work-orders/MSG-6j.md +0 -117
  308. package/work-orders/NOTIF-12.md +0 -214
  309. package/work-orders/NOTIF-13.md +0 -142
  310. package/work-orders/NOTIF-14.md +0 -151
  311. package/work-orders/PRIM-1.md +0 -177
  312. package/work-orders/SHELL-1.md +0 -255
  313. package/work-orders/SHELL-2.md +0 -197
  314. package/work-orders/SHELL-3.md +0 -359
  315. package/work-orders/SHELL-4.md +0 -351
  316. package/work-orders/SHELL-5.md +0 -345
  317. package/work-orders/SHELL-6.md +0 -210
  318. package/work-orders/THEME-1.md +0 -587
  319. package/work-orders/THEME-10.md +0 -303
  320. package/work-orders/THEME-11.md +0 -208
  321. package/work-orders/THEME-2.md +0 -339
  322. package/work-orders/THEME-3.md +0 -210
  323. package/work-orders/THEME-4.md +0 -271
  324. package/work-orders/THEME-5.md +0 -341
  325. package/work-orders/THEME-6.md +0 -247
  326. package/work-orders/THEME-7.md +0 -82
  327. package/work-orders/THEME-8.md +0 -211
  328. package/work-orders/THEME-9.md +0 -194
  329. package/work-orders/UCM-AUTH-10.md +0 -490
  330. package/work-orders/UCM-AUTH-11.md +0 -185
  331. package/work-orders/UCM-AUTH-12.md +0 -98
  332. package/work-orders/UCM-AUTH-13.md +0 -47
  333. package/work-orders/UCM-AUTH-9.md +0 -207
  334. package/work-orders/UCM-CHART-10.md +0 -150
  335. package/work-orders/UCM-CHART-11.md +0 -132
  336. package/work-orders/UCM-CHART-12.md +0 -348
  337. package/work-orders/UCM-CHART-13.md +0 -122
  338. package/work-orders/UCM-CHART-14.md +0 -198
  339. package/work-orders/UCM-CHART-15.md +0 -187
  340. package/work-orders/UCM-CHART-16.md +0 -169
  341. package/work-orders/UCM-CHART-17.md +0 -195
  342. package/work-orders/UCM-CHART-18.md +0 -200
  343. package/work-orders/UCM-CHART-8.md +0 -147
  344. package/work-orders/UCM-CHART-9.md +0 -265
  345. package/work-orders/UCM-CHAT-1.md +0 -190
  346. package/work-orders/UCM-CHAT-2.md +0 -75
  347. package/work-orders/UCM-DEP-1.md +0 -63
  348. package/work-orders/UCM-I18N-4.md +0 -226
  349. package/work-orders/UCM-I18N-5.md +0 -211
  350. package/work-orders/UCM-MSG-20.md +0 -192
  351. package/work-orders/UCM-PRIM-2.md +0 -114
  352. package/work-orders/UCM-SHELL-7.md +0 -134
  353. package/work-orders/UCM-SHELL-8.md +0 -125
  354. package/work-orders/UCM-TEST-1.md +0 -304
  355. package/work-orders/UCM-THEME-13.md +0 -69
  356. package/work-orders/UCM-THEME-14.md +0 -312
  357. package/work-orders/UCM-THEME-15.md +0 -356
  358. package/work-orders/UCM-THEME-16.md +0 -80
  359. package/work-orders/UCM-THEME-17.md +0 -111
  360. package/work-orders/UCM-THEME-18.md +0 -184
  361. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  362. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  363. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  364. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  365. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  366. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  367. package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
@@ -1,345 +0,0 @@
1
- # SHELL-5 — Promote the secondary-navigation shell
2
-
3
- **Target repo:** `ui-core-micha` (branch `main` — this repo has no `develop`)
4
- **Tier:** 3 (shared core)
5
- **Review:** independent `reviewer` **and** `ui_reviewer` — both mandatory, concurrent
6
- **Version target:** the next **minor** after `THEME-5` — a new exported component is additive API.
7
- Read the published version at implementation time; do **not** hardcode a number.
8
- **Prototype:** `work-orders/assets/SHELL-2-account-navigation.html` — the **composition** spec. The
9
- two-width rendered side-by-side is therefore a commit gate.
10
- **Strand:** `DS-11`. **Supersedes `SHELL-2`**, whose row moves to `dropped` — it described a different
11
- shape (inline in `AccountPage`, no promotion) and its measurements are superseded by the ones below.
12
- **Follow-ups, both named now:** `jg-ferien` migrates onto this and **deletes its own drawer**
13
- (operator requirement); the `AccountPage` users table is `AUTH-4`, deliberately not here.
14
-
15
- ---
16
-
17
- ## A. Envelope
18
-
19
- ### Goal
20
-
21
- After the primary destinations (the bottom bar, `SHELL-3`) comes the choice *within* a section.
22
- Two apps solve it separately: jg-ferien has `SectionSwitcher` (119 lines, grouped list + bottom
23
- drawer), and ucm's own `AccountPage` uses a horizontal `Tabs` strip that fails on a phone. Promote
24
- jg's shape, make `AccountPage` its first consumer, and let jg's copy be deleted afterwards.
25
-
26
- **Look and feel follow jg-ferien, by operator decision.** This is not a redesign — it is a promotion
27
- plus one new consumer.
28
-
29
- ### The driver, measured 2026-08-11 in cockpit at the current baseline
30
-
31
- Re-measured because `SHELL-2`'s numbers were three days old. All figures at **`innerWidth` 411 CSS px**
32
- — the "mobile" preset yields 411, not 375, so **at the real 375 target every number is worse**.
33
-
34
- | | measured |
35
- |---|---|
36
- | Sections rendered | **6** — Profile, Security, Users, Invite, Support, Notifications (the last is the app's `extraTab`) |
37
- | Strip container | 327 px |
38
- | Strip content | 566 px → **239 px overflow** |
39
- | Off the visible edge | **3 of 6** — Invite, Support, Notifications |
40
- | Scroll buttons | **none**; not in `variant="scrollable"` |
41
- | Scroller `overflow-x` | `auto` — so it **is** swipeable |
42
- | Desktop 1280 | 566 px used of 1136 px → **570 px headroom, zero overflow** |
43
-
44
- **Two corrections to the recorded claim, both from this measurement:**
45
-
46
- 1. **The sections are not unreachable.** `overflow-x: auto` means a horizontal swipe reaches them. The
47
- precise defect is that **there is no affordance whatsoever** — no arrows, no edge fade, and no
48
- scrollbar on a touch pointer. It is a discoverability failure, not an unreachability one. Say it
49
- that way; the stronger claim would not survive a reviewer.
50
- 2. **The desktop is not broken.** 570 px of headroom, and at ~94 px per tab the strip would only fill
51
- at about **twelve** sections. So the grouped sidebar on desktop is a **structural improvement, not
52
- a space fix** — it must be argued as organisation and scanability, never as "it does not fit".
53
-
54
- **Why a drawer rather than a better strip:** the section count is **not fixed and not knowable at
55
- design time.** `AccountPage` builds it from permissions — Profile and Security always, then Users,
56
- Invite, Support conditionally — plus whatever `extraTabs` the app injects (measured: cockpit 1,
57
- fitness-monitor 1, **jg 3**, so a jg superuser sees up to **eight**). No "fit N tabs" solution can be
58
- correct against a variable, permission-dependent count. That argument, not the 375 px measurement, is
59
- the load-bearing one.
60
-
61
- ### Scope
62
-
63
- #### Part 1 — the component
64
-
65
- Export `SectionNav` (name it whatever the kit's convention prefers; the point is that it is one
66
- component with a `mode`, not two). Derived from `jg-ferien/frontend/src/components/EventInfoHub/SectionSwitcher.jsx`,
67
- read at `e551ff7`.
68
-
69
- | Prop | Meaning |
70
- |---|---|
71
- | `mode` | `'desktop'` (sticky grouped sidebar) or `'mobile'` (bottom drawer) |
72
- | `groups` | `[{ key, label, items: [{ key, label }] }]` — labels are **finished strings**, as `SHELL-1`, `SHELL-3` and `DS-11` already settled |
73
- | `activeKey` / `onSelect` | the app derives the active key; the component does not guess |
74
- | `open` / `onClose` | drawer only |
75
- | `title` | drawer heading |
76
- | `overviewItem` | **optional** `{ label }` — renders jg's special first entry, selected when `activeKey == null` |
77
- | `rememberedKey` | **optional** — the "last opened" secondary line |
78
- | `headerOffset` | sticky top offset in the desktop mode |
79
- | `zIndex` | drawer only, default `theme.zIndex.drawer + 3` |
80
- | `sidebarWidth` | default `280` |
81
-
82
- **Each jg-specific concept becomes optional, and `AccountPage` is what proves it optional** — the same
83
- argument structure that worked in `SHELL-3`:
84
-
85
- - **The "Übersicht" entry is hardcoded today** (`SectionSwitcher.jsx:27-30`) as a separate `Paper` with
86
- one `ListItemButton`, selected when `activeKey == null`. That is jg's event-overview model.
87
- `AccountPage` has no such concept → `overviewItem`, omitted there.
88
- - **`rememberedKey`** and its "Zuletzt geöffnet" secondary line (`:50-53`) are jg-only.
89
- `AccountPage` omits it.
90
- - **`APP_HEADER_HEIGHT`** is imported from jg's own `mobileShell/constants` (`:13`, used at `:79` as
91
- `top: APP_HEADER_HEIGHT + 24`). ucm cannot know a host's header height → `headerOffset`.
92
- - **`zIndex.drawer + 3`** is hardcoded (`:95`). Keep **+3 as the default and document why**: `SHELL-3`'s
93
- bottom bar sits at `+2`, so the drawer must render above it. The ordering is meaningful, not
94
- arbitrary — a consumer that lowers it will put the drawer under the bar.
95
- - **The 280 px sidebar and the two-column grid live in the *consumer* today**
96
- (`EventInfoPage.jsx:1146`, `gridTemplateColumns: { md: "280px minmax(0, 1fr)" }`). **The component
97
- takes it over** and renders `children` beside the sidebar, so no consumer re-derives the desktop
98
- layout. This is the one place the promotion widens jg's component rather than copying it.
99
- - **German literal fallbacks go.** `t("EventInfoPage.OVERVIEW", "Übersicht")` and
100
- `t("EventInfoPage.LAST_OPENED_VIEW", "Zuletzt geöffnet")` disappear with finished-string labels.
101
- Note `t("SectionSwitcher.TITLE")` at `:75` has **no fallback at all** — in the kit that key must
102
- exist per the `SHELL-1` pattern (`userMenuTranslations`) or the raw key renders on screen.
103
-
104
- Keep from jg unchanged: the `18` px drawer top radius, `78dvh` max height, `p: 2`, and
105
- `pb: max(16px, env(safe-area-inset-bottom))` — the safe-area handling is **already correct there**.
106
-
107
- #### Part 2 — the trigger, which is net-new
108
-
109
- jg has **no trigger bar** — verified, its trigger sits in the page header. The 56 px trigger bar is
110
- specified only by `SHELL-2`'s prototype. It is therefore the one genuinely new piece, and it is what
111
- supplies the missing affordance the measurement found. Build it as part of this component (a
112
- consumer must not have to invent the thing that makes the drawer discoverable).
113
-
114
- #### Part 3 — `AccountPage` as the first consumer
115
-
116
- Replace the `Tabs` strip: grouped sidebar at `md` and up, trigger bar plus drawer below it. The
117
- existing `?tab=<value>` query param and every permission gate stay exactly as they are —
118
- `AccountPage` keeps computing which sections exist, the shell only presents them.
119
-
120
- **Grouping is a real decision, not a mechanical wrap.** Six to eight flat sections must become named
121
- groups; the prototype's composition is the spec. If it does not cover a section an app injects via
122
- `extraTabs`, those land in a final group — do not invent a name for someone else's tab.
123
-
124
- ### Non-goals / do not touch
125
-
126
- - **The users table** — `AUTH-4`. Measured today: 1053 px inside a 325 px container, the container
127
- scrolls, and **the document does not overflow**. Six fixed-width desktop columns on a phone is a
128
- content-density problem with a different fix class, and bundling it here would put an unbounded
129
- redesign into a promotion. `SHELL-2`'s 1045 px from 2026-08-08 still holds (+8 px) — cell padding is
130
- already the baseline's `10px 16px`, so `THEME-4`/`THEME-5` did not move it.
131
- - **jg-ferien's migration** — its own follow-up WO, which is what makes this a removal of duplication
132
- rather than an addition.
133
- - `AccountPage`'s permission logic, its `extraTabs` contract, and every section's content.
134
- - The bottom bar (`SHELL-3`/`SHELL-4`) and `UserMenu` (`SHELL-1`).
135
- - No baseline token change. The shell consumes tokens; it introduces none, so there is **no token
136
- delta list** — the prototype predates the design language, but `AccountPage` renders against the
137
- **host** theme, so composition comes from the prototype and every value from the app.
138
-
139
- ### Risks
140
-
141
- - **No staging net.** A version bump on `main` publishes; the two reviews are the only gate.
142
- - **`AccountPage` is mounted by at least ten apps** — bigler-consult, cockpit, fitness-monitor,
143
- hpc-bridge, hram, innoservice, jg-ferien, kerzenziehen, reimbursements. This changes the account
144
- screen's navigation everywhere at once. There is no way to stage it; the rendered gate and the
145
- reviews carry it.
146
- - **The safe-area padding cannot be verified.** A desktop 375 px viewport always reports an inset of
147
- zero — the wall `TPL-1` hit. jg's value is already correct; assert the CSS and record it unverified.
148
- - **Screenshot capture has failed repeatedly in this estate.** If it fails again, DOM/computed-style
149
- inspection is the declared substitution — and say so, because it can confirm a token resolved but
150
- not whether a screen reads well.
151
- - **The trigger bar is the only unproven piece.** Everything else is promotion of running code.
152
-
153
- ### Required tests to WRITE
154
-
155
- 1. `groups` renders one section per item, grouped, in order, with finished labels.
156
- 2. `activeKey` selects exactly the matching item; a key matching nothing selects none and does not
157
- throw; `activeKey == null` selects `overviewItem` **when it is supplied**.
158
- 3. `overviewItem` and `rememberedKey` omitted → neither renders, no empty `Paper`, no stray secondary
159
- line. **This is the "AccountPage proves them optional" test.**
160
- 4. `onSelect` fires with the item's key; selecting in the drawer closes it.
161
- 5. `mode="desktop"` renders the sidebar plus `children` in the two-column grid; `mode="mobile"`
162
- renders the trigger and no sidebar.
163
- 6. The drawer's default `zIndex` resolves **above** `SHELL-3`'s bar (`drawer + 3` vs `drawer + 2`).
164
- 7. Theme-agnosticism, per `SHELL-4`'s pattern: render under a `createAppTheme` theme **and** a plain
165
- `createTheme()` and assert resolved surface/divider values follow each. jg is a non-baseline
166
- consumer, so this is not hypothetical.
167
- 8. `THEME-4`'s shadowing check still returns no findings — any `sx` this component owns must not
168
- duplicate a baseline `styleOverrides` key.
169
- 9. `AccountPage`: all six cockpit-shaped sections reachable at 375 px without horizontal scrolling;
170
- the `?tab=` param still selects; permission-gated sections still absent when not permitted.
171
-
172
- Plus `tsc -p tsconfig.build.json --noEmit` clean. **No full-suite run** — affected set is the new
173
- component's spec, `AccountPage`'s, and the theme specs touched by test 8.
174
-
175
- ### Verification
176
-
177
- The **two-width rendered side-by-side at 375 px and 1280 px** against
178
- `assets/SHELL-2-account-navigation.html`, per the standing gate. What to look at: at 375 px the
179
- trigger must be visible without scrolling and the drawer must reach every section; at 1280 px the
180
- sidebar and content must sit in the two-column grid with no strip left behind.
181
-
182
- Measure, do not eyeball, the one number this WO exists to fix: **the strip's 239 px overflow must
183
- become zero**, and no section may sit outside its container. State whether real screenshots were
184
- obtained or DOM inspection substituted.
185
-
186
- ### Parity guardrail
187
-
188
- Presentation only. No permission, behaviour, data-contract or routing change; `?tab=` keeps working.
189
- Every section that renders today still renders, reached differently.
190
-
191
- ---
192
-
193
- ## B. Implementation map
194
-
195
- *Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
196
-
197
- ### Execution directive (read this first)
198
-
199
- > **If you are the implementer reading this work order as your own specification: this section is
200
- > NOT addressed to you.** It tells the Orchestrator how to invoke you. **You ARE that invocation —
201
- > do NOT shell out to `codex exec`.**
202
- >
203
- > Implement through `codex exec` in the background — invoked directly via Bash (never the
204
- > `debugger`/`*_coder` Agent wrappers) with BOTH flags `--skip-git-repo-check` and
205
- > `--dangerously-bypass-approvals-and-sandbox`. **Pass the WO via stdin, not as a positional
206
- > argument** (a positional arg hits Windows' command-line length limit — proven repeatedly in this
207
- > repo). Fallback to direct Claude implementation only on Codex quota/rate-limit/non-zero exit — and
208
- > note that the fallback flips authorship, so both reviewers stay mandatory either way.
209
- >
210
- > **Read `git log origin/main..HEAD` and `git status`, and re-read this WO's register row, before your
211
- > own review.** Across four ucm work orders the implementer produced an invalid review claim three
212
- > times — twice by self-committing with a self-report, once by writing a **fabricated** verdict into
213
- > `WORK_ORDERS.md` before either reviewer had started. The preamble now forbids editing that file;
214
- > verify it was respected.
215
-
216
- ### Context package
217
-
218
- **Precondition confirmed:** `THEME-5` landed and published (`8df44c5`, `package.json` reads `2.35.0`).
219
- Clean starting tree.
220
-
221
- **Reference implementation, read at `e551ff7`** (read-only, do not edit):
222
- `jg-ferien/frontend/src/components/EventInfoHub/SectionSwitcher.jsx` (119 lines, reproduced in full
223
- below for convenience — re-verify against the live file if it has moved) and its consumer
224
- `jg-ferien/frontend/src/pages/EventInfoPage.jsx:1110-1160` (the mode switch + the 280px desktop grid
225
- this WO's component takes over, `gridTemplateColumns: { md: "280px minmax(0, 1fr)" }`).
226
-
227
- ```jsx
228
- // SectionSwitcher.jsx — current jg source, annotated with what changes per the Envelope:
229
- function SectionSwitcherList({ groupedSections, activeKey, rememberedKey, onSelectSection }) {
230
- // renders: Paper(Übersicht item) -- ONLY IF overviewItem is supplied, see below --
231
- // then one Paper per group: group-head Box + List of ListItemButton(selected, primary=label,
232
- // secondary="Zuletzt geöffnet" when item.key===rememberedKey && !selected)
233
- }
234
- export default function SectionSwitcher({ mode, open=false, onClose, groupedSections, activeKey=null,
235
- rememberedKey=null, onSelectSection, title }) {
236
- // mode==='desktop': Box{position:'sticky', top: APP_HEADER_HEIGHT+24} wrapping SectionSwitcherList
237
- // else: Drawer{anchor:'bottom', open, onClose, sx:{zIndex: theme=>theme.zIndex.drawer+3},
238
- // PaperProps.sx:{borderTopLeftRadius:18, borderTopRightRadius:18, maxHeight:'78dvh', p:2,
239
- // pb:'max(16px, env(safe-area-inset-bottom))'}} containing a title Typography + scrollable list
240
- }
241
- ```
242
-
243
- **File location:** `src/layout/SectionNav.jsx` — this is page-chrome/navigation-shell, the same
244
- category as `src/layout/PageLayout.jsx`, not a leaf UI widget (`MobileBottomNav`'s placement in
245
- `src/components/` was already slightly inconsistent with this; use your judgement but `src/layout/`
246
- reads as the better fit here since this component, like `PageLayout`, wraps page content).
247
-
248
- **Component API — resolving one structural ambiguity the Envelope leaves implicit:** the prop table
249
- lists `open`/`onClose` unchanged from jg ("drawer only"), but jg's drawer was triggered by a button
250
- that lived OUTSIDE `SectionSwitcher` (in jg's own page header), whereas Part 2 requires the new
251
- trigger bar to be built INSIDE this component. A trigger owned by the component needs to be able to
252
- open a drawer whose `open` state jg's original code expected the CONSUMER to own — these two
253
- requirements only reconcile with the standard optional-controlled React pattern (the same shape MUI's
254
- own components use):
255
- ```js
256
- const [internalOpen, setInternalOpen] = useState(false);
257
- const isControlled = open !== undefined;
258
- const drawerOpen = isControlled ? open : internalOpen;
259
- const handleTriggerClick = () => { if (!isControlled) setInternalOpen(true); };
260
- const handleDrawerClose = () => { if (!isControlled) setInternalOpen(false); onClose?.(); };
261
- ```
262
- `AccountPage` (this WO's consumer) passes neither `open` nor `onClose` — fully uncontrolled, the
263
- trigger manages itself. This also leaves the door open for jg's later migration (a separate WO) to
264
- pass `open`/`onClose` in controlled mode if it turns out to still want its own external trigger
265
- instead of this component's built-in one — not solved here, just not foreclosed.
266
-
267
- **i18n split — two different kinds of text, do not conflate them:**
268
- - **Group/item labels are finished strings** the consumer supplies via the `groups` prop (per
269
- `SHELL-1`/`SHELL-3`/`DS-11`'s settled convention) — the component does not call `t()` on them.
270
- - **The component's own chrome text is NOT consumer-supplied and needs its own i18n keys**, registered
271
- in a new `src/i18n/sectionNavTranslations.ts`, following `src/i18n/userMenuTranslations.ts`'s exact
272
- shape (`{ 'SectionNav.KEY': { de, fr, en, sw } }`) and exported from `src/index.js`'s translations
273
- section (see `export { userMenuTranslations }`). Needed keys: a default drawer `title` (jg's
274
- `t("SectionSwitcher.TITLE")` **had no fallback at all** — the Envelope calls this out explicitly as
275
- a trap: in the kit that key must exist or the raw key renders on screen) and the trigger's "Bereich"
276
- eyebrow label (jg hardcodes this as static JSX text in the prototype, not a translation call today —
277
- give it one here, e.g. `SectionNav.TRIGGER_EYEBROW`). Both `title` and any future eyebrow-override
278
- stay optional props with these keys as the default, same pattern as `UserMenu`'s `profileLink`.
279
-
280
- **`kitSxRegistry.js` registration is mandatory, not optional — this is new territory `SHELL-3` predates
281
- knowing about:** `THEME-4`/`THEME-5` wired a hard-failing kit-wide check
282
- (`src/theme/themeCompleteness.js`: `assertKitSxDisjoint`, `reportKitSxBypasses`,
283
- `reportOffPaletteColours`) that runs against `src/theme/kitSxRegistry.js`. Any `sx` this new component
284
- owns for a baseline-styled MUI component (check `BASELINE_STYLED_MUI_COMPONENTS` in
285
- `themeCompleteness.js` for the 23-key list — `MuiDrawer`/`MuiPaper` are both on it) **must** be
286
- exported as a top-level `const X_SX = {...}` (follow `MOBILE_BOTTOM_NAV_ROOT_SX`'s exact shape) and
287
- added to `kitSxRegistry.js`'s imports/`SX_EXPORTS`/`KIT_COMPONENT_SX_REGISTRY` — an inline, unexported
288
- `sx={{ borderTopLeftRadius: 18, ... }}` on the `Drawer`'s `PaperProps` would otherwise trip
289
- `reportKitSxBypasses` in the affected-set test run. Also: no colour literal (`SHELL-4`/`THEME-5`'s
290
- `reportOffPaletteColours` scan) — jg's code already uses no hex here (all values are numeric/px/token
291
- strings), so this should be a non-issue if the port is faithful, but verify.
292
-
293
- **`zIndex` default:** keep jg's `theme.zIndex.drawer + 3` as the literal default (a plain JS function
294
- default parameter on the component, exactly like `MobileBottomNav`'s `defaultZIndex` — NOT registered
295
- in the theme, since `SHELL-4` established functions cannot live in the static baseline tree). The
296
- Envelope's own reasoning: `SHELL-3`'s bottom bar sits at `+2`, so this must render above it — document
297
- that ordering as a comment, do not silently pick a different offset.
298
-
299
- **`AccountPage.jsx` grouping** (`src/pages/AccountPage.jsx`, current flat `tabs` `useMemo` at
300
- `:125-160`, current `<Tabs>`/`<Tab>` block at `:196-206`): the prototype's footer states the fixed
301
- grouping explicitly — **"Mein Konto"** (`profile`, `security`), **"Verwaltung"** (`users`, `invite`),
302
- **"Hilfe"** (`support`), and a trailing **"Weitere"** catch-all for anything else (this is exactly
303
- where `extraTabs` without a named group lands — the prototype's own worked example is cockpit's
304
- "Benachrichtigungen" `extraTab` falling into "Weitere"). No standalone "Übersicht" entry — the
305
- prototype explicitly starts at the first group (panel 1's own caption: `AccountPage` has no overview
306
- page for one to point at) — so **do not pass `overviewItem`**. Group labels need their own i18n keys
307
- (new `Account.GROUP_MY_ACCOUNT` / `Account.GROUP_MANAGEMENT` / `Account.GROUP_HELP` /
308
- `Account.GROUP_MORE` — this repo already has an `Account.*` key family, e.g. `Account.TAB_PROFILE` at
309
- `:130` — follow that naming). Build the `groups` array by mapping the existing flat `tabs` list into
310
- these four buckets by `value`, preserving each tab's already-computed `label`; do not change the
311
- permission logic that produces `tabs` in the first place. `activeKey`/`onSelect` continue to be driven
312
- by the existing `safeTab`/`handleTabChange` — the `?tab=` contract and `activeTabExists` fallback logic
313
- at `:182-188` are unchanged (Non-goal, explicit parity guardrail).
314
-
315
- **Mode switching in `AccountPage`:** add `const isMobile = useMediaQuery(theme.breakpoints.down('md'))`
316
- (needs `useTheme`/`useMediaQuery` imports) — same breakpoint jg's `EventInfoPage.jsx:138` uses and the
317
- same one `SHELL-3`'s `MobileBottomNav` defaults `hideAbove` to, so this is the estate's established
318
- mobile/desktop split, not a new number to justify. Desktop: render `<SectionNav mode="desktop">`
319
- beside content in the two-column grid (the component now owns this grid — do not keep
320
- `AccountPage`'s own `gridTemplateColumns` if you add one, the component takes it over per Part 1's
321
- last bullet). Mobile: render `<SectionNav mode="mobile">` (uncontrolled, no `open`/`onClose` passed)
322
- ahead of the tab content — its own trigger renders first per the prototype ("the first thing rendered
323
- on mobile").
324
-
325
- **Dev harness:** add one specimen to `dev/entries.jsx` (follow the existing pattern, e.g.
326
- `MobileBottomNavEntry`) exercising both `mode="desktop"` and `mode="mobile"` side by side if
327
- practical, using the same four-group shape as the prototype, so the two-width verification below has
328
- something to point the preview browser at beyond `AccountPage` itself (which needs a full
329
- `AuthContext`/backend-shaped fixture to render meaningfully in the harness).
330
-
331
- ### Verification procedure
332
-
333
- Per `preview-running-app` skill: `ui-core-micha-dev` launch config exists (`.claude/launch.json`,
334
- port 5199) — a prior session in this repo could not get the Browser pane to composite frames at all
335
- (not a rendering bug, the pane itself did not display). Attempt it again fresh; if it fails again,
336
- DOM/computed-style inspection is the declared, accepted substitution per this WO's own Risks section
337
- — state which happened in the register note, and specifically capture the two numbers the WO frames
338
- as the actual deliverable: the strip's now-absent overflow (should be 0, there is no strip anymore)
339
- and confirm every section's item is reachable in the rendered `groups` list at 375px without
340
- horizontal scroll.
341
-
342
- ### Mini-handover
343
-
344
- Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
345
- WO: `work-orders/SHELL-5.md`. Follow `orchestrate-codex`.
@@ -1,210 +0,0 @@
1
- # SHELL-6 — A controlled `SectionNav` with no way to open must not render a trigger
2
-
3
- **Target repo:** `ui-core-micha` (branch `main` — this repo has no `develop`)
4
- **Tier:** 3 (shared core)
5
- **Review:** independent `reviewer` **and** `ui_reviewer` — both mandatory, concurrent
6
- **Version target:** the next **patch** after the published version. A defect fix, no API change, no
7
- consumer relying on the current behaviour. Read the published version at implementation time.
8
- **Unblocks:** `jg-ferien/NAV-37`, currently `blocked` (`c627d7b`)
9
- **Follows:** `SHELL-5` (`2.36.0`)
10
-
11
- ---
12
-
13
- ## A. Envelope
14
-
15
- ### Goal
16
-
17
- In `mode="mobile"`, `SectionNav` renders its built-in trigger **unconditionally**
18
- (`src/layout/SectionNav.jsx:246`). In controlled mode `handleTriggerClick` calls `onOpen?.()`
19
- (`:200-206`), so a consumer that drives `open` itself and passes no `onOpen` gets a button that is
20
- **visible, focusable and does nothing**.
21
-
22
- Make the trigger render only when it can actually open the drawer.
23
-
24
- ### This is a defect, not a missing configuration
25
-
26
- A control that receives focus and performs no action is wrong on its own terms — it is an
27
- accessibility defect independent of any consumer's wishes. That is why the fix is the **semantics**
28
- rather than a `showTrigger` prop: a prop would make the dead button the default for every controlled
29
- consumer who has not heard of the prop, turning a defect into a configuration option.
30
-
31
- **Nothing today relies on the current behaviour.** `AccountPage`, the only shipped consumer, is
32
- **uncontrolled** (it passes no `open`), so its trigger keeps rendering unchanged. `jg-ferien` is the
33
- consumer that hit this and is blocked on it.
34
-
35
- ### How it shipped, and the rule that follows
36
-
37
- `SHELL-5`'s Envelope specified that the component *has* a trigger and never said *when it renders*.
38
- The implementation made it unconditional — correctly, given the spec. `NAV-37` then assumed that
39
- omitting `onOpen` suppresses it; that assumption appears nowhere in `SHELL-5`.
40
-
41
- **The uncovered cell and the untested cell were the same cell.** Every existing mobile test is either
42
- uncontrolled (`:192`, `:210`) or controlled *with* `onOpen` (`:161`) — verified. So this fix breaks no
43
- existing test, which is the point: the case was never specified, therefore never tested, therefore
44
- shipped.
45
-
46
- **Hence the second deliverable below: write the render matrix down.** A prop table plus a scope
47
- narrative reads complete and is not.
48
-
49
- ### Scope
50
-
51
- **1. The fix.** In the mobile return path, render the `<ButtonBase>` trigger only when a click can do
52
- something: uncontrolled (the component owns `internalOpen`), or controlled **and** `onOpen` supplied.
53
- `children` and the `<Drawer>` keep rendering in every case — `jg-ferien` needs exactly that: its own
54
- triggers, the shared drawer, its content.
55
-
56
- **2. The render matrix, in the component's own doc comment.** This is not optional documentation; it
57
- is the artefact whose absence caused the defect.
58
-
59
- | `mode` | `open` | `onOpen` | trigger | sidebar | `children` | drawer |
60
- |---|---|---|---|---|---|---|
61
- | `desktop` | ignored | ignored | — | yes | yes, in the grid | — |
62
- | `mobile` | absent (uncontrolled) | ignored | **yes** | — | yes | yes, internal state |
63
- | `mobile` | given | given | **yes** | — | yes | caller-driven |
64
- | `mobile` | given | **absent** | **no** ← this WO | — | yes | caller-driven |
65
-
66
- Plus the optional-part cells, all already correct and worth recording so they stay that way:
67
- `overviewItem` absent → no overview entry and no empty `Paper`; `rememberedKey` absent → no secondary
68
- line; `title` absent → `t('SectionNav.TITLE')`; `triggerEyebrow` absent → `t('SectionNav.TRIGGER_EYEBROW')`.
69
-
70
- ### Non-goals / do not touch
71
-
72
- - **No new prop.** Not `showTrigger`, not `hideTrigger`. The operator considered and rejected both:
73
- they would leave the dead button as the default. A consumer wanting "controlled **with** `onOpen` but
74
- my own trigger too" has no representation after this WO — that case has no consumer today and adding
75
- a prop for it is the speculative generality the YAGNI guardrail rejects.
76
- - **The desktop path** — untouched, including the grid, `sidebarWidth` and `headerOffset`.
77
- - The drawer, its `zIndex` resolution, its safe-area padding, `SectionNavList`, and every label or
78
- i18n key.
79
- - `AccountPage` — no change; it is uncontrolled and keeps its trigger.
80
- - No baseline token change, no `kitSxRegistry` change. `SECTION_NAV_TRIGGER_SX` stays registered:
81
- `THEME-4`'s check is about key disjointness, not about whether an element renders.
82
-
83
- ### Risks
84
-
85
- - **It is a behaviour change to a shipped component**, which is why "no consumer relies on it" is
86
- stated above rather than assumed. If a consumer outside this repo passes `open` without `onOpen` and
87
- wants the trigger, it breaks for them — verified as nobody today.
88
- - **No staging net.** A version bump on `main` publishes; the two reviews are the only gate.
89
- - **The condition must be `onOpen` supplied, not `open` supplied.** Keying it on the wrong prop would
90
- remove the trigger from the uncontrolled case as well, which is `AccountPage`'s only affordance.
91
- Test 3 exists for that.
92
-
93
- ### Required tests to WRITE
94
-
95
- Extend `tests/SectionNav.test.jsx`.
96
-
97
- 1. **The fix, with non-vacuity.** `mode="mobile"` + `open` + **no** `onOpen` → **no** trigger button
98
- in the DOM, while `children` still render and the drawer still responds to `open`. **This test must
99
- fail against the current published version** — say so in the WO's landing note, since a fix whose
100
- test passes before the fix is not a fix.
101
- 2. Controlled **with** `onOpen` → trigger renders, clicking calls `onOpen`. (The existing `:161`
102
- already covers the call; assert the rendering explicitly.)
103
- 3. **Uncontrolled** → trigger renders and clicking opens the internal drawer. This is the guard against
104
- keying the condition on the wrong prop.
105
- 4. Desktop is unchanged in all three prop combinations.
106
- 5. `THEME-4`'s shadowing check still returns no findings.
107
-
108
- Plus `tsc -p tsconfig.build.json --noEmit` clean. **No full-suite run** — affected set is
109
- `tests/SectionNav.test.jsx` and the theme spec touched by test 5.
110
-
111
- ### Verification
112
-
113
- The change is the **absence** of an element in one prop combination, so the rendered gate has little to
114
- add beyond the tests: assert the DOM, and confirm `AccountPage`'s own trigger still renders at 375 px.
115
- If capture is unavailable, DOM inspection is the declared substitution — and here it is not a
116
- fallback but the appropriate instrument, because "no button" is a DOM fact, not an appearance.
117
-
118
- ### Parity guardrail
119
-
120
- `AccountPage` renders identically. The only intended change is that a controlled consumer without
121
- `onOpen` no longer gets a dead button.
122
-
123
- ---
124
-
125
- ## B. Implementation map
126
-
127
- *Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
128
-
129
- ### Execution directive (read this first)
130
-
131
- > **If you are the implementer reading this work order as your own specification: this section is
132
- > NOT addressed to you.** It tells the Orchestrator how to invoke you. **You ARE that invocation —
133
- > do NOT shell out to `codex exec`.**
134
- >
135
- > Implement through `codex exec` in the background — invoked directly via Bash (never the
136
- > `debugger`/`*_coder` Agent wrappers) with BOTH flags `--skip-git-repo-check` and
137
- > `--dangerously-bypass-approvals-and-sandbox`. **Pass the WO via stdin, not as a positional
138
- > argument.** Fallback to direct Claude implementation only on Codex quota/rate-limit/non-zero exit —
139
- > and that flips authorship, so both reviewers stay mandatory either way.
140
- >
141
- > **Do NOT edit `WORK_ORDERS.md`**, and before your own review read `git log origin/main..HEAD` plus
142
- > `git status`. Check the working tree first: `NAV-37`'s rejected CSS workaround lives uncommitted in
143
- > `jg-ferien`, and other sessions have been active in this repo throughout this strand.
144
-
145
- ### Context package
146
-
147
- **Precondition confirmed:** `SHELL-5` landed and published (`b283c33`/`ada72be`); `I18N-1` also landed
148
- independently in the interim (`a6d322d`). Clean starting tree, no implementer commit on top.
149
-
150
- **Current file state** (`src/layout/SectionNav.jsx`, verified — line numbers drifted a little from
151
- the WO's own `:246`/`:200-206` estimate since `SHELL-5`'s own review-fix pass, but the structure is
152
- identical):
153
- ```js
154
- // :188-190
155
- const [internalOpen, setInternalOpen] = useState(false);
156
- const isControlled = open !== undefined;
157
- const drawerOpen = isControlled ? open : internalOpen;
158
- // :200-206
159
- const handleTriggerClick = () => {
160
- if (isControlled) {
161
- onOpen?.();
162
- } else {
163
- setInternalOpen(true);
164
- }
165
- };
166
- // :244-266, the mobile return path
167
- return (
168
- <>
169
- <ButtonBase sx={SECTION_NAV_TRIGGER_SX} onClick={handleTriggerClick} ...>
170
- ...
171
- </ButtonBase>
172
- <Box sx={SECTION_NAV_CONTENT_SX}>{children}</Box>
173
- <Drawer ...>...</Drawer>
174
- </>
175
- );
176
- ```
177
-
178
- **The fix, precisely:** add `const canOpenTrigger = !isControlled || Boolean(onOpen);` and wrap only
179
- the `<ButtonBase>` block in `{canOpenTrigger && (...)}`. `children` and `<Drawer>` stay unconditional
180
- — do not touch their rendering. Do not key the condition on `open` (the WO's own Risks section flags
181
- this exact mistake as the guard test 3 exists for) — the condition is about whether a click can do
182
- anything, which depends on `isControlled`/`onOpen`, never on the current `open` value itself.
183
-
184
- **The render-matrix doc comment (scope item 2):** add a JSDoc-style comment directly above
185
- `export function SectionNav(...)` reproducing the table from the Envelope (`mode`/`open`/`onOpen` →
186
- trigger/sidebar/children/drawer), plus the "optional-part cells" paragraph (`overviewItem` absent →
187
- no overview entry; `rememberedKey` absent → no secondary line; `title`/`triggerEyebrow` absent →
188
- their `t()` fallbacks). This is a required deliverable per the WO's own framing ("not optional
189
- documentation; it is the artefact whose absence caused the defect"), not a nice-to-have.
190
-
191
- **Test file:** `tests/SectionNav.test.jsx` already has a controlled-mode test from `SHELL-5`'s own
192
- review-fix round (`'defers to the caller in controlled mode: the trigger calls onOpen, not internal
193
- state'`, roughly `:145-175` now) — that test passes `onOpen`, so it exercises the WO's "controlled
194
- with `onOpen`" cell, not the new "controlled without `onOpen`" cell this WO fixes. Add the four new
195
- tests as siblings of it, following its existing render helper (`renderNav`) and prop-passing style.
196
- Required test 1 (non-vacuity) needs to be run against a build BEFORE the fix lands to confirm it
197
- fails — since this is a live working tree with the pre-fix code already checked out, this is simply:
198
- write the test first, run it once to confirm it fails, then apply the one-line fix and confirm it
199
- passes. State this sequence happened in the register note.
200
-
201
- **Invariants:** no new prop (`showTrigger`/`hideTrigger` explicitly rejected by the WO); desktop path,
202
- `Drawer`, `zIndex`, safe-area padding, `SectionNavList`, all i18n keys, `AccountPage` itself, and
203
- `kitSxRegistry.js` all untouched. `SECTION_NAV_TRIGGER_SX` stays registered even though the element
204
- it styles now sometimes doesn't render — `THEME-4`'s check is about key disjointness, not element
205
- presence, so this is a non-issue, not a discrepancy to resolve.
206
-
207
- ### Mini-handover
208
-
209
- Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
210
- WO: `work-orders/SHELL-6.md`. Unblocks `jg-ferien/NAV-37`. Follow `orchestrate-codex`.