Skip to content

feat(web-console): enforce mockup-to-production visual parity - #470

Open
kdoberst wants to merge 6 commits into
openshift-online:mainfrom
kdoberst:GitHub-469-ui-specs-and-mockups
Open

kdoberst wants to merge 6 commits into
openshift-online:mainfrom
kdoberst:GitHub-469-ui-specs-and-mockups

Conversation

@kdoberst

@kdoberst kdoberst commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Problem: The repository had no enforced workflow for proving that materially changed web-console pages match their approved mockups. Storybook captures could also rely on live or fixture data without a clear production-data boundary.
  • Fix: Add a UI build gate that requires deterministic Storybook parity capture, visual review, and validation. Add shell-free parity stories and page-surface components for the gateway list and provision-gateway pages.
  • Alternatives considered: None

Breaking change? No

New to this repo

  • Storybook screenshot parity gate using fixed desktop and narrow viewports, static-network checks, capture manifests, review attestations, and completion validation.
  • Production data-boundary checker that rejects fixture imports, demo data, scenario selectors, and live runtime hooks in parity stories.
  • Shell-free page-surface stories that compare page-owned UI while excluding shared application chrome.

Tracking

#469

Specs and other PRs

Related specs and dependent PRs

Components changed

  • Web-console Storybook and application styling
  • Web-console build and reconciliation skills
  • Web-console UI specification and verification guidance
  • Gateway mockups and Storybook parity stories

Changes with impact

  • ⚠️ MEDIUM: Web-console implementation workflows now treat visual parity evidence as a required completion condition for pages with approved mockups.
  • ⚠️ MEDIUM: Storybook parity captures now require deterministic data, shell-free page surfaces, matching region contracts, and no non-Storybook network requests.
  • ✅ LOW: PatternFly utility styles are loaded in the production and mockup Storybook previews and the web-console root.
  • ✅ LOW: Gateway list and provision-gateway mockups were reorganized around reusable page-surface components and parity stories.

Verification

  • Unit/integration tests created or updated
  • Error paths considered and addressed
  • Code changes match spec or acceptance criteria
  • Code changes match Jira

Screenshots / video

Miro diagram of flow

Video demo

Questions for discussion

  • None

Details

Technical details (for humans and bots)

The first commit adds the repository-level agent guidance, the ui-build-gate skill, deterministic Storybook harness guidance, a production/story data-boundary checker, and the visual parity gate script. The gate builds both Storybooks, verifies the required parity stories, captures production and mockup images at 1440x900 and 390x844, checks that requests stay within local Storybook assets, and records hashes and review history in visual-parity.json.

The second commit extracts page-owned surfaces from the gateway mockups and adds Parity/Gateways/Gateway list and Parity/Gateways/Provision gateway stories. This lets reviewers compare page structure, backgrounds, spacing, content, and responsive behavior without treating shared masthead or application shell chrome as page-owned UI.

The gate also requires the production route to use its real data source while the Storybook story supplies deterministic props or adapters. A failed visual comparison must include a structured difference ledger and a subsequent source change before another capture can run.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Central YAML (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7bb111f1-0b20-48fb-b954-5deed052e9f8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@kdoberst kdoberst added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 7, 2026
@hypershell-delivery

hypershell-delivery Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Amber review: comment

Amber review

Status: Complete

View the submitted review.

hypershell-delivery[bot]

This comment was marked as outdated.

@hypershell-delivery hypershell-delivery Bot added the amber/changes-requested Amber requested changes on this PR label Oct 7, 2026
@kdoberst
kdoberst force-pushed the GitHub-469-ui-specs-and-mockups branch 2 times, most recently from ea13039 to 1c5ea58 Compare October 7, 2026 21:01
hypershell-delivery[bot]

This comment was marked as outdated.

@hypershell-delivery hypershell-delivery Bot removed the amber/changes-requested Amber requested changes on this PR label Oct 7, 2026
hypershell-delivery[bot]

This comment was marked as outdated.

@kdoberst kdoberst removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 8, 2026
@kdoberst
kdoberst force-pushed the GitHub-469-ui-specs-and-mockups branch from ac48432 to ff62f30 Compare October 8, 2026 15:44
hypershell-delivery[bot]

This comment was marked as outdated.

hypershell-delivery[bot]

This comment was marked as outdated.

@kdoberst
kdoberst force-pushed the GitHub-469-ui-specs-and-mockups branch from dcf2474 to 8bd90be Compare October 9, 2026 16:00
hypershell-delivery[bot]

This comment was marked as outdated.

@hypershell-delivery hypershell-delivery Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict

The prior Major (provision-gateway design-review diverging from the enforced parity surface) is now resolved: the Mockups/ story renders the shell-wrapped page surface, so the approved design and the locked reference match. The swap left ProvisionGatewayMockup and its showLocalDevelopment branch unreferenced, which is the one new (Minor) nit.

Findings

Dead code left behind after the provision-gateway story swap. The fix pointed Mockups/Gateways/Provision gateway at ProvisionGatewayParityMockup (provision-gateway.stories.tsx:3-7), which correctly unifies the design-review and parity surfaces. But ProvisionGatewayMockup (provision-gateway.tsx:25-27) is now exported and referenced nowhere, so the roles of the two mockups simply flipped - the previously-unused ParityMockup is now live and ProvisionGatewayMockup is now the dead export. Along with it, the showLocalDevelopment branch in ProvisionGatewayForm (provision-gateway.tsx:17-22) is only reachable through that dead function, and the LocalDevelopment story was deleted, so the local-kind design-review variant is no longer rendered by any story. Either delete ProvisionGatewayMockup and the now-unreachable showLocalDevelopment path, or re-attach the local-development variant to a live story so it stays reviewable.

Cross-PR coordination

No material cross-PR coordination issue requires maintainer action.

Previous concerns

  1. Header region requires literal variant="default" - Addressed. gateway-list-page-surface.tsx:10-14 and provision-gateway-page-surface.tsx:14-18 write variant="default" on the data-page-region="header" <PageSection>, matching the check at visual_parity_gate.py:161.
  2. No --mockup-view target satisfies both shell-free and region checks - Addressed. The region-bearing JSX lives in the shell-free gateway-list-page-surface.tsx / provision-gateway-page-surface.tsx (no shell imports), while MockupShell/MockupTemplate usage lives in gateway-list.tsx / provision-gateway.tsx.
  3. Parity stories omit the mandated MockupParityFrame - Addressed. gateway-list.parity.stories.tsx:9-13 and provision-gateway.parity.stories.tsx:9-13 wrap the surface in <MockupParityFrame> via render.
  4. Viewport-only screenshots miss below-the-fold parity - Addressed. visual_parity_gate.py:293 passes --full-page to the capture.
  5. Provision-gateway Mockups/ design-review story diverges from the enforced parity surface - Addressed. provision-gateway.stories.tsx:3-7 now renders ProvisionGatewayParityMockup, which wraps ProvisionGatewayPageSurface in MockupShell (provision-gateway.tsx:29-31), so the approved design and the enforced reference are the same surface, as they already were for gateway-list. (New residual dead code tracked in Findings above.)
  6. Prior Minor - shared HealthStatus caller passes no appearance - Still present. gateway-list.tsx:153 renders <HealthStatus label={gateway.status} /> with no appearance, so every row falls back to the default good (green success) treatment regardless of the status string. Harmless with the all-Healthy fixtures, but the shared component's warning/danger paths are never exercised by the canonical example.

Findings Summary (ordered by severity, highest first)

  1. [Minor] ProvisionGatewayMockup is now unreferenced dead code after the story swap, and its showLocalDevelopment branch / deleted LocalDevelopment story leave the local-kind variant unrendered by any story - Example Consistency (provision-gateway.tsx:25-27, provision-gateway.tsx:17-22).
  2. [Minor] Shared HealthStatus caller passes no appearance, so every row renders the green good treatment regardless of status - Example Consistency (gateway-list.tsx:153).

Convention Checklist

Convention Result
No panic() in production code N/A (no Go changes)
No em dashes in text files Pass
Conventional commit messages Pass
Examples consistent with documented conventions Pass (prior divergence resolved)
No dead/unreferenced example code Fail (ProvisionGatewayMockup)
PatternFly reuse via shared components Pass
Tooling passes against its own shipped fixtures Pass

return <Form aria-label="Provision gateway" isWidthLimited><FormGroup isRequired label="Gateway name" fieldId="gateway-name"><TextInput id="gateway-name" isRequired onChange={(_event, value) => setName(value)} value={name} validated={showValidationErrors && !name ? "error" : "default"} />{showValidationErrors ? <FormHelperText><HelperText><HelperTextItem screenReaderText="error status" variant="error">This field is required.</HelperTextItem></HelperText></FormHelperText> : null}</FormGroup>{!showLocalDevelopment ? <FormGroup isRequired label="Network access" fieldId="network-access"><Gallery hasGutter minWidths={{ default: "250px", md: "300px" }} role="radiogroup"><Choice name="placement" value="public" selected={!localKind && network === "public"} title="Public" description="Accessible through a public endpoint." onChoose={() => { setLocalKind(false); setNetwork("public"); }} /><Choice name="placement" value="vpn" selected={!localKind && network === "vpn"} title="VPN" description="For gateways that need to reach GitLab and other internal Red Hat services." descriptionLabel="Red Hat VPN required" onChoose={() => { setLocalKind(false); setNetwork("vpn"); setProvider("aws"); }} /></Gallery></FormGroup> : <FormGroup isRequired label="Local development" fieldId="local-development"><Gallery hasGutter minWidths={{ default: "250px", md: "300px" }} role="radiogroup"><Choice name="placement" value="local-kind" selected={localKind} title="Use local-kind" description="Use the local Kind cluster for development." onChoose={() => setLocalKind(true)} /></Gallery></FormGroup>}{network ? <FormGroup isRequired label="Cloud provider" fieldId="cloud-provider"><Gallery hasGutter minWidths={{ default: "250px", md: "300px" }} role="radiogroup"><Choice name="provider" value="aws" selected={provider === "aws"} title="Amazon Web Services" description="For workloads that rely heavily on AWS services or data." icon={awsLogo} iconPadding="1rem 0" onChoose={() => setProvider("aws")} /><Choice name="provider" value="ibm" selected={provider === "ibm"} title="IBM Cloud" description="The default home for gateways. General-purpose workloads with no special network or data needs." icon={ibmCloudLogo} isDisabled={network === "vpn"} onChoose={() => setProvider("ibm")} /></Gallery></FormGroup> : null}<p>{showLocalDevelopment ? "The local Kind cluster is used for development." : "A matching managed cluster is selected at random for the chosen network and provider."}</p><ActionGroup><Button type="submit" variant="primary">Provision gateway</Button><Button type="button" variant="link">Cancel</Button></ActionGroup></Form>;
}

export function ProvisionGatewayMockup({ showValidationErrors = false, showLocalDevelopment = false }: { showValidationErrors?: boolean; showLocalDevelopment?: boolean }) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Minor] Dead code after the story swap. With provision-gateway.stories.tsx now rendering ProvisionGatewayParityMockup, this ProvisionGatewayMockup export is referenced nowhere - the dead/live roles of the two mockups simply flipped. Its showLocalDevelopment branch in ProvisionGatewayForm (lines 17-22) is only reachable through this function, and the LocalDevelopment story was removed, so the local-kind variant is no longer rendered by any story. Delete ProvisionGatewayMockup and the now-unreachable showLocalDevelopment path, or re-attach the local-development variant to a live story.

This branch has not been deployed

No deployments
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.

1 participant