Skip to content

Implement cohesive Storybook for staff + community UI portal application package Fixes #266#283

Open
rohit-r-kumar wants to merge 266 commits into
mainfrom
rohit-r-kumar/issue266
Open

Implement cohesive Storybook for staff + community UI portal application package Fixes #266#283
rohit-r-kumar wants to merge 266 commits into
mainfrom
rohit-r-kumar/issue266

Conversation

@rohit-r-kumar

@rohit-r-kumar rohit-r-kumar commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #266

Summary by Sourcery

Add a cohesive Storybook setup for the staff UI portal and align shared component stories to the new structure.

New Features:

  • Introduce Storybook configuration, global decorators, and Apollo mocks for the ui-staff app.
  • Add Storybook stories for staff root header and login page components.

Bug Fixes:

  • Adjust staff app tests to mock staff permissions directly and avoid brittle expectations around Navigate redirects.
  • Ensure placeholder pages fall back to JWT roles when backend-derived permissions are unavailable.

Enhancements:

  • Document and skip an unsupported SectionLayout behaviour test that depends on JWT roles-derived permissions.
  • Retitle existing shared UI Storybook stories to follow a consistent Components/Pages taxonomy across staff and shared packages.

Build:

  • Add Storybook, Chromatic, and related addons as dev dependencies and expose Storybook build/run/Chromatic scripts for the ui-staff app.
  • Align ui-staff-shared Storybook React dependency to the workspace catalog version.
  • Register @storybook/react in the workspace catalog for shared use.

ttrang-nguyen and others added 30 commits April 29, 2026 11:03
Agent-Logs-Url: https://github.com/CellixJs/cellixjs/sessions/b332a369-259e-49fd-b45e-84b75c4b82b5

Co-authored-by: ttrang-nguyen <126544378+ttrang-nguyen@users.noreply.github.com>
ui-staff-route-shared -> ui-staff-shared
ui-components -> ui-shared
Agent-Logs-Url: https://github.com/CellixJs/cellixjs/sessions/3d00d38e-39b6-480b-8f35-9451c37a4c08

Co-authored-by: rohit-r-kumar <175348946+rohit-r-kumar@users.noreply.github.com>
- Add StaffAppRoles constants (Staff.TechAdmin, Staff.Finance,
  Staff.ServiceLineOwner, Staff.CaseManager)
- Add centralized staffRouteRoles mapping route paths to required roles
- Extract extractRoles helper to staff-app-roles.ts (shared utility)
- Add RequireRole component that reads StaffAuthContext and redirects
  to /unauthorized when user lacks the required role
- Wrap each /staff/* route in App.tsx with RequireRole using the
  centralized mapping
- Filter nav links in StaffRouteShell to only show sections the user
  can access based on their roles
- Add react-router-dom and react-dom to ui-staff-shared dependencies
- Add unit tests for StaffAppRoles, staffRouteRoles, extractRoles,
  and RequireRole component

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…a app roles (#214)

Tests cover every role × route combination:
- Staff.TechAdmin: /staff/tech allowed, all others blocked
- Staff.Finance: /staff/finance allowed, all others blocked
- Staff.CaseManager: /staff/community + /staff/users allowed, others blocked
- Staff.ServiceLineOwner: /staff/community + /staff/users allowed, others blocked
- No roles: all /staff/* routes blocked

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove antd and react-dom from dependencies where unused, and storybook
from devDependencies across all ui-staff-route-* packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…zation

Covers all 4 Entra app-role × 4 route combinations (20 access tests)
plus 3 nav-link visibility tests = 23 tests total.

Strategy: inject a fake OIDC session into sessionStorage via
page.addInitScript() before the app loads. oidc-client-ts restores
stored users without re-validating the JWT signature, so each test
can control the user's roles independently without restarting the
mock auth server.

Test matrix:
- Staff.TechAdmin:       /tech ✓, /finance ✗, /community ✗, /users ✗
- Staff.Finance:         /finance ✓, /tech ✗, /community ✗, /users ✗
- Staff.CaseManager:     /community ✓, /users ✓, /finance ✗, /tech ✗
- Staff.ServiceLineOwner:/community ✓, /users ✓, /finance ✗, /tech ✗
- No roles:              all 4 routes ✗

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…enityJS

feat: add new test commands for end-to-end and acceptance tests in turbo.json

style: format vitest.config.ts for consistency in quotes and indentation
@rohit-r-kumar rohit-r-kumar requested a review from a team June 17, 2026 17:35
@sourcery-ai

sourcery-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a cohesive Storybook setup for the staff UI portal app, wires it into the monorepo tooling, and adjusts auth/permissions handling and story naming to better support SSR, Storybook, and future role-based behaviour.

Sequence diagram for Storybook staff story rendering with global decorators

sequenceDiagram
    participant Storybook
    participant Decorator as GlobalDecorator
    participant Helmet as HelmetProvider
    participant Auth as AuthProvider
    participant Theme as ThemeProvider
    participant Apollo as MockedProvider
    participant Router as MemoryRouter
    participant Story

    Storybook->>Decorator: render(Story, context)
    Decorator->>Decorator: read context.parameters
    Decorator->>Helmet: wrap children
    Helmet->>Auth: wrap children with mockOidcConfig
    Auth->>Theme: wrap children
    Theme->>Apollo: wrap children with mocks, defaultOptions
    Apollo->>Router: wrap children with initialEntries
    Router->>Story: render
    Story-->>Storybook: rendered staff UI story
Loading

File-Level Changes

Change Details Files
Stabilize App SSR tests by mocking staff permissions and relaxing Navigate expectations.
  • Replace StaffAuthProvider mock with a useStaffPermissions hook mock that returns all permissions and avoids Apollo during SSR.
  • Update the staff index navigation test to only assert that rendering succeeds instead of expecting concrete RootSection content, with comments explaining Navigate behavior.
apps/ui-staff/src/App.test.tsx
Add Storybook configuration and tooling for the ui-staff app, including shared packages and Chromatic integration.
  • Introduce .storybook/main.ts with react-vite framework, shared story globs (app, ui-shared, ui-staff-*), and addons including Chromatic, docs, onboarding, a11y, vitest, and Apollo.
  • Add .storybook/preview.tsx that wires HelmetProvider, OIDC AuthProvider, ThemeProvider, MockedProvider, MemoryRouter, global Apollo mocks, layout, controls, apolloClient, story sorting, and a11y parameters.
  • Provide apollo-mocks.ts as a shared empty mocks fallback and a placeholder vitest.setup.ts for addon-vitest.
  • Extend ui-staff package.json with Storybook, build-storybook, and chromatic scripts and add Storybook- and Chromatic-related devDependencies.
  • Update pnpm-workspace catalog and lockfile entries to align Storybook versioning across the monorepo.
apps/ui-staff/.storybook/main.ts
apps/ui-staff/.storybook/preview.tsx
apps/ui-staff/.storybook/apollo-mocks.ts
apps/ui-staff/.storybook/vitest.setup.ts
apps/ui-staff/package.json
pnpm-workspace.yaml
pnpm-lock.yaml
Improve auth/permissions fallback logic in shared staff UI components and align tests with current behavior.
  • Change PlaceholderPage permission resolution to fall back to auth.roles when auth.permissions is absent, ensuring placeholders still show roles derived directly from JWT.
  • Skip and document the SectionLayout test that expected JWT roles to drive menu items when backend permissions are unavailable, noting this as desired future behavior rather than current implementation.
packages/ocom/ui-staff-shared/src/index.tsx
packages/ocom/ui-staff-shared/src/section-layout.test.tsx
Standardize Storybook story titles/namespacing for shared UI components to fit a cohesive Components/Pages taxonomy.
  • Retitle LoggedInUser-related stories under Components/Shared/Logged In User with consistent subpaths for Logged In and Not Logged In states.
  • Retitle shared dropdown, header, navigation, and vertical-tabs stories under Components/Shared/... to match the new hierarchy.
  • Retitle staff-specific stories for RequireRole and SectionLayoutContainer under Components/Staff/... to mirror the shared naming scheme.
  • Add Header and LoginPage stories to the staff root route package under Components/Staff/Root/Header and Pages/Staff/Root/Login respectively.
packages/ocom/ui-shared/src/components/molecules/logged-in-user/logged-in-user.stories.tsx
packages/ocom/ui-shared/src/components/molecules/logged-in-user/logged-in.stories.tsx
packages/ocom/ui-shared/src/components/molecules/logged-in-user/not-logged-in.stories.tsx
packages/ocom/ui-shared/src/components/organisms/dropdown-menu/communities-dropdown.container.stories.tsx
packages/ocom/ui-shared/src/components/organisms/dropdown-menu/communities-dropdown.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/handle-logout.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/logged-in-user-community.container.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/logged-in-user-community.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/logged-in-user-root.container.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/logged-in-user-root.stories.tsx
packages/ocom/ui-shared/src/components/organisms/header/logged-in-user.container.stories.tsx
packages/ocom/ui-shared/src/components/organisms/navigation/menu-component.stories.tsx
packages/ocom/ui-shared/src/components/organisms/vertical-tabs/index.stories.tsx
packages/ocom/ui-staff-shared/src/require-role.stories.tsx
packages/ocom/ui-staff-shared/src/section-layout.container.stories.tsx
packages/ocom/ui-staff-route-root/src/components/header.stories.tsx
packages/ocom/ui-staff-route-root/src/pages/login-page.stories.tsx
Align staff shared Storybook dependency with monorepo catalog.
  • Update @storybook/react devDependency in ui-staff-shared package.json to use the pnpm catalog version instead of a pinned 9.x version.
packages/ocom/ui-staff-shared/package.json

Assessment against linked issues

Issue Objective Addressed Explanation
#266 Implement full Storybook support for the @apps/ui-staff portal app, including Storybook config in the app package, discovery of stories from @apps/ui-staff, @ocom/ui-shared, and @ocom/ui-staff-* packages, portal-level decorators/mocks, and package scripts to run, build, and publish Storybook (Chromatic).
#266 Implement full Storybook support for the @apps/ui-community portal app, with its own Storybook configuration, appropriate story discovery, decorators/mocks, and scripts analogous to the staff portal. The PR only adds Storybook configuration, decorators, and scripts under apps/ui-staff; there are no changes under apps/ui-community or its package.json.
#266 Ensure stories are organized and titled so that each portal Storybook has exactly two top-level sections (Pages and Components), with page stories under Pages and component stories under Components, while keeping stories colocated with their owning source and excluding @cellix/ui-core stories. The PR updates many staff-related and shared story titles to fall under Components/* and adds a Pages/Staff/Root/Login story, and stories remain colocated. However, it only partially adjusts titles and organization for the staff side and does not demonstrate that all stories (for both portals) are constrained to exactly Pages and Components, nor does it touch community stories; the issue’s requirement applies to both portals.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In .storybook/preview.tsx, when building defaultOptions for MockedProvider you overwrite watchQuery and query completely; consider shallow-merging those nested objects with any apolloParams.defaultOptions passed in so story-specific cache/policy configuration isn’t lost.
  • In .storybook/main.ts, most addons are resolved via getAbsolutePath but storybook-addon-apollo-client is left as a bare string; for consistency and to avoid resolution issues in the monorepo/PnP setup, you may want to resolve that addon via getAbsolutePath as well.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `.storybook/preview.tsx`, when building `defaultOptions` for `MockedProvider` you overwrite `watchQuery` and `query` completely; consider shallow-merging those nested objects with any `apolloParams.defaultOptions` passed in so story-specific cache/policy configuration isn’t lost.
- In `.storybook/main.ts`, most addons are resolved via `getAbsolutePath` but `storybook-addon-apollo-client` is left as a bare string; for consistency and to avoid resolution issues in the monorepo/PnP setup, you may want to resolve that addon via `getAbsolutePath` as well.

## Individual Comments

### Comment 1
<location path="packages/ocom/ui-staff-shared/src/index.tsx" line_range="27-28" />
<code_context>
 	const resolvedPermissions = React.useMemo(() => {
 		if (explicitRoles && explicitRoles.length > 0) return explicitRoles;
 		const perms = auth?.permissions;
-		if (!perms) return [];
-		return Object.entries(perms)
-			.filter(([, isEnabled]) => isEnabled === true)
-			.map(([permKey]) => permKey);
+		if (perms) {
+			return Object.entries(perms)
+				.filter(([, isEnabled]) => isEnabled === true)
</code_context>
<issue_to_address>
**issue (bug_risk):** Fallback to roles is skipped when permissions exist but have no enabled entries.

With this change, `auth.roles` is only used when `auth.permissions` is falsy. If the backend sets `permissions` to an object (possibly empty, or with all `false` values) while resolving, `perms` is truthy and you’ll return an empty array instead of falling back to `auth.roles`, which can under-authorize users. It’d be safer to detect the "no enabled permissions" case (e.g., after filtering enabled entries) and then fall back to `auth.roles` instead of returning an empty list.
</issue_to_address>

### Comment 2
<location path="apps/ui-staff/.storybook/preview.tsx" line_range="31" />
<code_context>
+		const initialEntries = context.parameters?.memoryRouter?.initialEntries ?? ['/'];
+		const apolloParams = context.parameters?.apolloClient ?? {};
+		const mocks = apolloParams.mocks ?? apolloMocks;
+		const { defaultOptions } = apolloParams;
+
+		return (
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Merging Apollo `defaultOptions` overwrites any existing `watchQuery`/`query` options from stories.

In `MockedProvider`, the current spread:
```ts
defaultOptions={{
  ...defaultOptions,
  watchQuery: { fetchPolicy: 'network-only' },
  query: { fetchPolicy: 'network-only' },
}}
```
overwrites any `watchQuery`/`query` options from `parameters.apolloClient.defaultOptions` (e.g. `errorPolicy`, `pollInterval`). To preserve those while enforcing the fetch policy, deep-merge the sub-objects instead:
```ts
defaultOptions={{
  ...defaultOptions,
  watchQuery: { ...(defaultOptions?.watchQuery ?? {}), fetchPolicy: 'network-only' },
  query: { ...(defaultOptions?.query ?? {}), fetchPolicy: 'network-only' },
}}
```
</issue_to_address>

### Comment 3
<location path="apps/ui-staff/.storybook/preview.tsx" line_range="78-82" />
<code_context>
+		},
+	},
+
+	a11y: {
+		// 'todo' - show a11y violations in the test UI only
+		// 'error' - fail CI on a11y violations
+		// 'off' - skip a11y checks entirely
+		test: 'todo',
+	},
+};
</code_context>
<issue_to_address>
**question (bug_risk):** Use of `a11y.test` option may not align with addon-a11y’s expected configuration.

The `a11y` block is using `test: 'todo'`, but `@storybook/addon-a11y` doesn’t expose a `test` option—its config is usually via `config`, `manual`, etc. Unless you have custom wiring that consumes `a11y.test`, this setting will be ignored and you won’t get the intended “todo/error/off” behavior. Please verify the addon’s current API and either update this config or add an adapter that maps `'todo' | 'error' | 'off'` to the real addon options.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 27 to +28
const perms = auth?.permissions;
if (!perms) return [];
return Object.entries(perms)
.filter(([, isEnabled]) => isEnabled === true)
.map(([permKey]) => permKey);
if (perms) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Fallback to roles is skipped when permissions exist but have no enabled entries.

With this change, auth.roles is only used when auth.permissions is falsy. If the backend sets permissions to an object (possibly empty, or with all false values) while resolving, perms is truthy and you’ll return an empty array instead of falling back to auth.roles, which can under-authorize users. It’d be safer to detect the "no enabled permissions" case (e.g., after filtering enabled entries) and then fall back to auth.roles instead of returning an empty list.

const initialEntries = context.parameters?.memoryRouter?.initialEntries ?? ['/'];
const apolloParams = context.parameters?.apolloClient ?? {};
const mocks = apolloParams.mocks ?? apolloMocks;
const { defaultOptions } = apolloParams;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Merging Apollo defaultOptions overwrites any existing watchQuery/query options from stories.

In MockedProvider, the current spread:

defaultOptions={{
  ...defaultOptions,
  watchQuery: { fetchPolicy: 'network-only' },
  query: { fetchPolicy: 'network-only' },
}}

overwrites any watchQuery/query options from parameters.apolloClient.defaultOptions (e.g. errorPolicy, pollInterval). To preserve those while enforcing the fetch policy, deep-merge the sub-objects instead:

defaultOptions={{
  ...defaultOptions,
  watchQuery: { ...(defaultOptions?.watchQuery ?? {}), fetchPolicy: 'network-only' },
  query: { ...(defaultOptions?.query ?? {}), fetchPolicy: 'network-only' },
}}

Comment on lines +78 to +82
a11y: {
// 'todo' - show a11y violations in the test UI only
// 'error' - fail CI on a11y violations
// 'off' - skip a11y checks entirely
test: 'todo',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Use of a11y.test option may not align with addon-a11y’s expected configuration.

The a11y block is using test: 'todo', but @storybook/addon-a11y doesn’t expose a test option—its config is usually via config, manual, etc. Unless you have custom wiring that consumes a11y.test, this setting will be ignored and you won’t get the intended “todo/error/off” behavior. Please verify the addon’s current API and either update this config or add an adapter that maps 'todo' | 'error' | 'off' to the real addon options.

@rohit-r-kumar rohit-r-kumar requested a review from a team as a code owner June 17, 2026 17:52
@rohit-r-kumar rohit-r-kumar changed the title Implement cohesive Storybook for staff UI portal application package Fixes #266 Implement cohesive Storybook for staff + community UI portal application package Fixes #266 Jun 17, 2026
Comment thread packages/ocom/ui-staff-shared/src/require-role.stories.tsx
Comment thread packages/ocom/ui-staff-route-user-management/.storybook/main.ts Outdated
Comment thread packages/ocom/ui-staff-route-user-management/vitest.config.ts
Comment thread packages/ocom/ui-staff-route-user-management/src/index.tsx
Comment thread packages/ocom/ui-community-route-admin/src/pages/members-accounts.stories.tsx Outdated
Comment thread pnpm-workspace.yaml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement cohesive Storybook per UI portal application package

4 participants