Skip to content

Template: Application Conventions

This template is used during Phase A of the Use Case to UX workflow. Copy it to your project directory and fill in the <!-- REPLACE: ... --> placeholders.

When filled in, the document title should follow the pattern: <Entity Domain Name> UX — Application Conventions.

Companion documents: Scope Analysis, Story Specifications (produced alongside this document in Phase A).


DocumentPathNotes
Entity Data Authority (GEN::ENT-DA)/documentation/src/content/docs/product/general-behaviors/entity-behaviors/entity-data-authority.mdGeneric CRUD behaviors all entity stories inherit
Backend API (<!-- REPLACE: resource -->)
Scope AnalysisScope decisions (SD-N) and open-question resolutions (OQ-N)
Story SpecificationsPer-story play function assertions and file inventory
Existing Storybook

FileBuilt ByContent
use-cases/reference/<!-- REPLACE: domain -->/_shared/types.ts
use-cases/reference/<!-- REPLACE: domain -->/_shared/mock-data.ts
use-cases/reference/<!-- REPLACE: domain -->/_shared/msw-handlers.ts
use-cases/reference/<!-- REPLACE: domain -->/_shared/<!-- REPLACE: page.tsx -->
use-cases/reference/<!-- REPLACE: domain -->/_shared/<!-- REPLACE: other.tsx -->

HandlerRouteMethodAdded ByLatencyNotes
<!-- REPLACE: /api/arda/entity/query -->POST
<!-- REPLACE: /api/arda/entity/lookup -->GET
<!-- REPLACE: /api/arda/entity -->POST
<!-- REPLACE: /api/arda/entity/:entityId -->GET
<!-- REPLACE: /api/arda/entity/:entityId -->PUT
<!-- REPLACE: /api/arda/entity/:entityId -->DELETE

Error response shape (standard across all endpoints):

{ "ok": false, "status": <!-- REPLACE: HTTP status code -->, "error": "<!-- REPLACE: human-readable message -->" }

IDSummaryAffectsDetails
SD-1scope-analysis.md#sd-1
SD-2scope-analysis.md#sd-2

All stories in this domain use the following title path format:

Use Cases/Reference/<!-- REPLACE: Domain Display Name -->/<Use Case Group>/<Story Title>
  • Domain Display Name:
  • Use Case Group:
  • Story Title:

Description MDX pages append /Description to the group path.

ArtifactPatternExample
Story file<scenario-slug>.stories.tsx
Description MDX<use-case-group-slug>.mdx
Shared component<component-name>.tsx
Shared typestypes.ts_shared/types.ts
Mock datamock-data.ts_shared/mock-data.ts
MSW handlersmsw-handlers.ts_shared/msw-handlers.ts

All story files and shared infrastructure live under:

/ux-prototype/src/use-cases/reference/<!-- REPLACE: domain-slug -->/

The _shared/ prefix on the shared infrastructure directory is intentional: the leading underscore causes Storybook to omit the directory from the sidebar navigation tree.


All stories use the fullAppProviders decorator, which supplies:

  • The application router context (enables sidebar navigation, usePathname, and deep-link args)
  • The MSW service worker (for handler interception)
  • The global theme and design-system providers (Tailwind theme tokens, Radix theme context)
const meta: Meta<typeof ComponentName> = {
title: '...',
component: ComponentName,
decorators: [fullAppProviders],
};

When a story renders multiple independent UI regions simultaneously (e.g., a data grid and an open side drawer), use within() to scope queries to the correct container. This prevents “found multiple elements with text X” errors.

const drawer = await canvas.findByRole('dialog');
const drawerPanel = within(drawer);
await expect(drawerPanel.getByRole('heading', { name: '<!-- REPLACE: heading -->' })).toBeVisible();

Re-query the container after mutations that may re-render it — stale element references cause test failures in CI.

ContainerHow to queryUse for

6.3 Portal-Aware Queries (screen vs. canvas)

Section titled “6.3 Portal-Aware Queries (screen vs. canvas)”

Some components render content outside the Storybook canvasElement by portalling to document.body. Use the global screen object for these elements.

Rule: if a query on canvas returns zero results for an element you can see on screen, switch to screen.

ComponentRenders inCorrect query
document.bodyscreen.findByRole('menuitem', { name: '...' })
document.bodyconst el = await screen.findByText('...') then await waitFor(() => expect(el).toBeVisible())