inspector/clients/web/.storybook/preview.tsx
cliffhall 0b64460097 chore(scripts): rename the local pre-push gate off ci, and guard it out of CI (#2146)
The root `ci` script was never what runs in GitHub CI, and the name worked
against us three ways: it collides with the built-in `npm ci` (which does
something else entirely and is one keystroke away), it invites the
cross-engine passes into a workflow by looking like the thing a workflow
should call, and it blurs "GitHub CI" and "the local gate" in every doc
that names it.

- Rename `ci` → `local:gate` and `ci:storybook` → `local:storybook`, with
  no back-compat alias: `npm run ci` now fails with npm's missing-script
  error, which prints the available scripts.

- Add `scripts/lib/workflow-gate.mjs` + its `test:scripts` suite, which
  runs over the repo's real `.github/workflows/**` and fails on an
  invocation of a `local:*` script, of a non-Chromium engine pass (derived
  from SUPPORTED_BROWSERS), or of `smoke:web:engine`, or on `SMOKE_BROWSER`
  set to anything but a literal `chromium`. It does not forbid
  `npm run smoke`, `smoke:web:chromium` or `smoke:tui` — those belong in CI
  and are there today. Comments are not scanned, so the workflow can still
  explain itself. This is the durable half; the rest is prose.

- Document the two-tier model once, in the root README ("Two tiers: GitHub
  CI and the local gate"), and point the other docs at it rather than
  restating the split.

- Sweep the ~40 stale references across README, AGENTS.md,
  .github/copilot-instructions.md, the client READMEs, the roadmap doc and
  the script headers. AGENTS.md and copilot-instructions.md change
  together, per the mirror rule.

No gate changes what it actually runs.

Signed-off-by: Cliff Hall <cliff@futurescale.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-08-26 23:56:34 -04:00

99 lines
3 KiB
TypeScript

import type { Preview } from "@storybook/react-vite";
import { MantineProvider, useMantineColorScheme } from "@mantine/core";
import { Notifications } from "@mantine/notifications";
import "@mantine/core/styles.css";
import "@mantine/notifications/styles.css";
import "../src/App.css";
import { theme } from "../src/theme/theme";
import { cssVariablesResolver } from "../src/theme/cssVariables";
import { useEffect } from "react";
// eslint-disable-next-line react-refresh/only-export-components
function ColorSchemeWrapper({
colorScheme,
children,
}: {
colorScheme: "light" | "dark";
children: React.ReactNode;
}) {
const { setColorScheme } = useMantineColorScheme();
useEffect(() => {
setColorScheme(colorScheme);
}, [colorScheme, setColorScheme]);
return <>{children}</>;
}
const preview: Preview = {
globalTypes: {
colorScheme: {
description: "Mantine color scheme",
toolbar: {
title: "Color Scheme",
icon: "mirror",
items: [
{ value: "light", title: "Light", icon: "sun" },
{ value: "dark", title: "Dark", icon: "moon" },
],
dynamicTitle: true,
},
},
},
initialGlobals: {
colorScheme: "light",
},
decorators: [
(Story, context) => {
const isFullscreen = context.parameters?.layout === "fullscreen";
return (
<MantineProvider
theme={theme}
defaultColorScheme="light"
cssVariablesResolver={cssVariablesResolver}
>
<ColorSchemeWrapper
colorScheme={context.globals.colorScheme ?? "light"}
>
<Notifications position="top-right" />
{isFullscreen ? (
<div style={{ height: "100vh", overflow: "hidden" }}>
<Story />
</div>
) : (
<Story />
)}
</ColorSchemeWrapper>
</MantineProvider>
);
},
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
a11y: {
// `'error'` fails the Storybook play-function tests (part of `npm run local:gate`)
// on any axe violation, so the zero-violation state this PR reached is
// enforced going forward rather than being a point-in-time result.
test: "error",
// Stories render presentational components (and whole screens) in
// isolation, outside the app's `AppShell` — which is what provides the
// page landmarks (`<main>`, nav) in the real app. The `region` rule
// ("all content must live inside a landmark") is therefore a page-level
// concern that can't be satisfied in Storybook isolation and would fire on
// essentially every story; it's covered by the app shell at runtime, not
// by the components under test. Disable it here so the a11y panel surfaces
// only rules the components can actually own.
config: {
rules: [{ id: "region", enabled: false }],
},
},
layout: "centered",
},
};
export default preview;