executable-stories-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,238 @@
1
+ # executable-stories-react
2
+
3
+ React components for rendering [executable-stories](https://github.com/jagreehal/executable-stories) StoryReport JSON. Drop into any React host app — Next.js, Astro, Vite, Remix, SvelteKit-as-host — and turn your test runs into living documentation that's also readable by humans, screen readers, and AI agents.
4
+
5
+ ```tsx
6
+ import { Report, parseStoryReport } from "executable-stories-react";
7
+ import "executable-stories-react/styles.css";
8
+
9
+ import reportJson from "./story-report.json";
10
+
11
+ const result = parseStoryReport(reportJson);
12
+
13
+ export default function Page() {
14
+ return <Report report={result} />;
15
+ }
16
+ ```
17
+
18
+ ## What this is for
19
+
20
+ `executable-stories` lets your real tests double as living documentation: every `given/when/then` step, every `kv/code/table/mermaid/section` doc entry, every screenshot, every tag becomes a permanent record of how the system behaves. The formatters CLI emits that record as `story-report.json`.
21
+
22
+ **This package renders that JSON inside your existing React app**, with no fetching, no setup, and full SSR support.
23
+
24
+ - Drop into your docs portal — engineers, designers, and product see one canonical, always-current spec.
25
+ - Drop into your internal dashboard — failures get triaged from the same place you ship from.
26
+ - Static-render it for AI agents — the output is plain semantic HTML they can parse without executing JavaScript.
27
+
28
+ ## Two flavors
29
+
30
+ ### `<Report>` — static, pure SSR
31
+
32
+ Server-renders to fully semantic HTML. No client JS required for content; works in Next.js Server Components, Astro static pages, and `react-dom/server.renderToString`. Best for: docs sites, AI-readable static exports, print, low-JS environments.
33
+
34
+ ```tsx
35
+ import { Report, parseStoryReport } from "executable-stories-react";
36
+ import "executable-stories-react/styles.css";
37
+
38
+ export default async function Page() {
39
+ const raw = await readFile("story-report.json", "utf8");
40
+ const result = parseStoryReport(JSON.parse(raw));
41
+ return <Report report={result} />;
42
+ }
43
+ ```
44
+
45
+ ### `<ReportInteractive>` — loaded with chrome
46
+
47
+ Adds live search (`/` to focus), failure jump (`f`), deep-link auto-scroll, sticky failure banner, keyboard shortcut help (`?`). Same data, same primitives, plus the affordances engineers want when triaging.
48
+
49
+ ```tsx
50
+ "use client";
51
+ import { parseStoryReport } from "executable-stories-react";
52
+ import { ReportInteractive } from "executable-stories-react/interactive";
53
+ import "executable-stories-react/styles.css";
54
+
55
+ export function Triage({ json }: { json: unknown }) {
56
+ return <ReportInteractive report={parseStoryReport(json)} />;
57
+ }
58
+ ```
59
+
60
+ > `<ReportInteractive>` lives at `executable-stories-react/interactive` so Next.js App Router can statically detect the `"use client"` boundary. The static `<Report>` import stays server-safe.
61
+
62
+ ## Getting started
63
+
64
+ ```bash
65
+ pnpm add executable-stories-react executable-stories-formatters
66
+ ```
67
+
68
+ Then:
69
+
70
+ 1. Run your tests with one of the framework adapters (vitest, jest, playwright, cypress, etc.).
71
+ 2. Run `executable-stories format --format story-report-json` to emit `story-report.json`.
72
+ 3. Import the JSON in your React app and pass it to `<Report>` or `<ReportInteractive>`.
73
+
74
+ The package has **two peer dependencies**: `react >=18` and `react-dom >=18`. Tested with React 19.
75
+
76
+ ## Customizing
77
+
78
+ ### Custom doc-entry types
79
+
80
+ `story.custom({ type: "chart", data: ... })` is the canonical escape hatch for user-defined doc content. Provide a renderer:
81
+
82
+ ```tsx
83
+ <Report
84
+ report={result}
85
+ customRenderers={{
86
+ chart: (entry) => <MyChart spec={entry.data} />,
87
+ "trace-waterfall": (entry) => <Waterfall spans={entry.data} />,
88
+ }}
89
+ />
90
+ ```
91
+
92
+ Unknown types fall back to a `<pre>` JSON dump.
93
+
94
+ ### Overriding the heavy built-ins
95
+
96
+ `mermaid`, `code`, and `section` are the doc kinds where you might reasonably want to ship your own rendering (you already use shiki in your site, you have a custom mermaid integration, etc.):
97
+
98
+ ```tsx
99
+ <Report
100
+ report={result}
101
+ renderers={{
102
+ mermaid: (entry) => <YourMermaid code={entry.code} />,
103
+ code: (entry) => <Shiki code={entry.content} lang={entry.lang} />,
104
+ }}
105
+ />
106
+ ```
107
+
108
+ Other kinds (`note`, `tag`, `kv`, `table`, `link`, `screenshot`) are not overridable via this prop — drop down to the primitives if you need a full structural override.
109
+
110
+ ### Composing your own layout
111
+
112
+ Every primitive is exported. Build whatever you want:
113
+
114
+ ```tsx
115
+ import {
116
+ ReportRoot, ReportSummary, ReportFeatureList,
117
+ useReport,
118
+ } from "executable-stories-react";
119
+
120
+ function MyCustomReport({ report }) {
121
+ return (
122
+ <ReportRoot report={report}>
123
+ <aside>
124
+ <ReportSummary />
125
+ <FailureSidebar />
126
+ </aside>
127
+ <main>
128
+ <ReportFeatureList />
129
+ </main>
130
+ </ReportRoot>
131
+ );
132
+ }
133
+
134
+ function FailureSidebar() {
135
+ const report = useReport();
136
+ const failed = report.features.flatMap(f =>
137
+ f.scenarios.filter(s => s.status === "failed")
138
+ );
139
+ return (
140
+ <ul>
141
+ {failed.map(s => <li key={s.id}><a href={`#${s.id}`}>{s.title}</a></li>)}
142
+ </ul>
143
+ );
144
+ }
145
+ ```
146
+
147
+ ### Theming
148
+
149
+ Theme via CSS custom properties on `:root` or any parent of the report:
150
+
151
+ ```css
152
+ :root {
153
+ --es-color-passed: oklch(72% 0.16 145);
154
+ --es-color-failed: oklch(64% 0.20 25);
155
+ --es-radius: 0.25rem;
156
+ --es-font-body: "Inter", system-ui;
157
+ }
158
+ ```
159
+
160
+ Auto dark/light via `prefers-color-scheme`. Force a scheme with `data-theme="dark"` or `data-theme="light"` on any ancestor.
161
+
162
+ The same `--es-*` tokens are emitted by the standalone HTML formatter — overrides on your site theme both reports consistently.
163
+
164
+ ## SSR / framework integration
165
+
166
+ ### Next.js App Router
167
+
168
+ ```tsx
169
+ // app/report/page.tsx
170
+ import { Report, parseStoryReport } from "executable-stories-react";
171
+ import "executable-stories-react/styles.css";
172
+ import { readFile } from "node:fs/promises";
173
+
174
+ export default async function ReportPage() {
175
+ const raw = await readFile("./story-report.json", "utf8");
176
+ return <Report report={parseStoryReport(JSON.parse(raw))} />;
177
+ }
178
+ ```
179
+
180
+ For interactivity, wrap a client component:
181
+
182
+ ```tsx
183
+ // app/report/page.tsx (server)
184
+ import { ClientReport } from "./client";
185
+
186
+ export default async function Page() {
187
+ const raw = await readFile("./story-report.json", "utf8");
188
+ return <ClientReport json={JSON.parse(raw)} />;
189
+ }
190
+
191
+ // app/report/client.tsx
192
+ "use client";
193
+ import { parseStoryReport } from "executable-stories-react";
194
+ import { ReportInteractive } from "executable-stories-react/interactive";
195
+ import "executable-stories-react/styles.css";
196
+
197
+ export function ClientReport({ json }: { json: unknown }) {
198
+ return <ReportInteractive report={parseStoryReport(json)} />;
199
+ }
200
+ ```
201
+
202
+ ### Astro Starlight
203
+
204
+ Use `<Report>` directly in `.astro` files (it's framework-agnostic semantic HTML on the server). Use `<ReportInteractive>` with `client:visible` if you want the live search.
205
+
206
+ ```astro
207
+ ---
208
+ import { Report, parseStoryReport } from "executable-stories-react";
209
+ import data from "../public/story-report.json";
210
+
211
+ const result = parseStoryReport(data);
212
+ ---
213
+ <Report report={result} />
214
+ ```
215
+
216
+ ### Vite / static export
217
+
218
+ Both `<Report>` and `<ReportInteractive>` work with vanilla Vite + React 19. The static page produced by `renderToString` is fully self-contained.
219
+
220
+ ## What you get for free
221
+
222
+ - **Stable IDs** for deep linking: `#feature-todos--add-a-todo` always scrolls to that scenario.
223
+ - **Schema validation** at the boundary: `parseStoryReport(unknown): Result<StoryReport>`. Wrong major version, missing fields, unknown doc kinds — all caught with a `<ReportSchemaError>` instead of a runtime crash.
224
+ - **AI-readable static output**: scenario titles in `<h3>`, errors in `<pre role="alert">`, mermaid source in `<pre data-mermaid>`, code in `<pre><code class="language-X">`. A language model can answer "what's failing?" from the raw HTML.
225
+ - **Print stylesheet**: docs are documents.
226
+ - **No bundler config**: tsup ESM+CJS, react/react-dom externalized.
227
+
228
+ ## Compatibility
229
+
230
+ | Schema | Package |
231
+ |--------|---------|
232
+ | `StoryReport` v1.x | `executable-stories-react` 0.x |
233
+
234
+ `parseStoryReport` accepts any 1.x report. A 2.x report renders `<ReportSchemaError>` with an upgrade hint — see `SCHEMA_VERSION_MISMATCH` in the result error code.
235
+
236
+ ## License
237
+
238
+ MIT — see [LICENSE](../../LICENSE).