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 +238 -0
- package/dist/index.cjs +1021 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +964 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive.cjs +846 -0
- package/dist/interactive.cjs.map +1 -0
- package/dist/interactive.d.cts +116 -0
- package/dist/interactive.d.ts +116 -0
- package/dist/interactive.js +820 -0
- package/dist/interactive.js.map +1 -0
- package/dist/parse.cjs +495 -0
- package/dist/parse.cjs.map +1 -0
- package/dist/parse.d.cts +51 -0
- package/dist/parse.d.ts +51 -0
- package/dist/parse.js +464 -0
- package/dist/parse.js.map +1 -0
- package/dist/styles.css +244 -0
- package/package.json +84 -0
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).
|