Background
In #7100 we set up Storybook for component rendering. To keep stories isolated, we had to stub common/utils/utils because the real module triggers a webpack initialisation error in Storybook:
utils → common/stores/account-store → common/constants → … → utils
The circular chain crashes the bundler at module init time.
The current workaround lives in frontend/.storybook/stubs/utils.js — a hand-rolled subset of Utils with simplified implementations of:
colour
GUID
fromParam
escapeHtml
getFlagsmithHasFeature / getFlagsmithValue / getPlansPermission
isSaas
keys.isEscape
safeParseEventValue
Problem
Stories render against the stub, not production Utils. Behaviour can drift:
- The stub's
GUID is a Math.random() shim, not whatever production uses (uuid?).
- The stub's
fromParam does a basic URLSearchParams parse and won't handle nested keys, repeat params, or type coercion the same way production does.
- The stub's
colour happens to match production today but won't pick up future changes (dark-mode-aware fallbacks, etc.).
- Any new
Utils.* method added to production has to be manually mirrored in the stub or stories using it will break.
This makes Storybook a less reliable visual reference for components that use Utils.
Proposed fix
Break the circular dependency in production so Storybook can import the real common/utils/utils:
- Identify the cycle. The stub comment points at
utils → account-store → constants. Map the full chain and decide which edge to cut.
- Likely candidates:
- Move whatever
utils.tsx needs from account-store into a small data-only module that doesn't pull constants back in.
- Or split
utils.tsx into a leaf module (no store imports) and a shell that re-exports + adds store-aware helpers — only the shell would be unsafe to import in Storybook, but most stories don't need those helpers.
- Once the cycle is gone, drop
frontend/.storybook/stubs/utils.js and remove the alias from frontend/.storybook/main.js.
- Verify stories still render and that the legacy
.js components depending on Utils (Input.js, etc.) still work in stories.
Acceptance criteria
Notes
Background
In #7100 we set up Storybook for component rendering. To keep stories isolated, we had to stub
common/utils/utilsbecause the real module triggers a webpack initialisation error in Storybook:The circular chain crashes the bundler at module init time.
The current workaround lives in
frontend/.storybook/stubs/utils.js— a hand-rolled subset ofUtilswith simplified implementations of:colourGUIDfromParamescapeHtmlgetFlagsmithHasFeature/getFlagsmithValue/getPlansPermissionisSaaskeys.isEscapesafeParseEventValueProblem
Stories render against the stub, not production
Utils. Behaviour can drift:GUIDis aMath.random()shim, not whatever production uses (uuid?).fromParamdoes a basicURLSearchParamsparse and won't handle nested keys, repeat params, or type coercion the same way production does.colourhappens to match production today but won't pick up future changes (dark-mode-aware fallbacks, etc.).Utils.*method added to production has to be manually mirrored in the stub or stories using it will break.This makes Storybook a less reliable visual reference for components that use
Utils.Proposed fix
Break the circular dependency in production so Storybook can import the real
common/utils/utils:utils → account-store → constants. Map the full chain and decide which edge to cut.utils.tsxneeds fromaccount-storeinto a small data-only module that doesn't pullconstantsback in.utils.tsxinto a leaf module (no store imports) and a shell that re-exports + adds store-aware helpers — only the shell would be unsafe to import in Storybook, but most stories don't need those helpers.frontend/.storybook/stubs/utils.jsand remove the alias fromfrontend/.storybook/main.js..jscomponents depending onUtils(Input.js, etc.) still work in stories.Acceptance criteria
frontend/.storybook/stubs/utils.jsis deleted.frontend/.storybook/main.jsno longer aliasescommon/utils/utilsto a stub.npm run storybookboots cleanly and all stories render.Utils.colour,Utils.GUID, andUtils.fromParam(ToggleChip, Checkbox, Tabs) — same visual behaviour as before.Notes