@onlook/storybook-plugin 0.4.0-beta.1 → 0.4.0-beta.10
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 +1 -1
- package/dist/cli/index.js +199 -52
- package/dist/index.d.ts +94 -21
- package/dist/index.js +835 -235
- package/dist/preset/index.d.ts +10 -0
- package/dist/preset/index.js +1345 -0
- package/dist/preview/index.d.ts +156 -0
- package/dist/preview/index.js +302 -0
- package/dist/screenshot-service/index.d.ts +0 -6
- package/dist/screenshot-service/index.js +197 -50
- package/dist/screenshot-service/utils/browser/index.d.ts +0 -6
- package/dist/screenshot-service/utils/browser/index.js +83 -14
- package/dist/storybook-onlook-plugin-CigILdDb.d.ts +61 -0
- package/package.json +19 -9
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/** Advertised + embedded in every event payload. Mirrored CLI-side (U8). */
|
|
4
|
+
declare const ONLOOK_CONTAINMENT_CONTRACT_VERSION = 1;
|
|
5
|
+
/** Marker attached to every contained export, thrown error, and the global registry. */
|
|
6
|
+
type ContainedModuleMarker = {
|
|
7
|
+
modulePath: string;
|
|
8
|
+
reason: 'env-validation';
|
|
9
|
+
envKeys: string[];
|
|
10
|
+
schemaKind?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Handshake global, set by the preview annotations (in `beforeAll` and again
|
|
14
|
+
* defensively when the decorator first runs):
|
|
15
|
+
* `globalThis.__ONLOOK_CONTAINMENT_CONTRACT__ = { version: 1 }`.
|
|
16
|
+
*/
|
|
17
|
+
declare const ONLOOK_CONTAINMENT_CONTRACT_GLOBAL = "__ONLOOK_CONTAINMENT_CONTRACT__";
|
|
18
|
+
type OnlookContainmentContract = {
|
|
19
|
+
version: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Emitted when the error-boundary decorator catches a render-time throw and
|
|
23
|
+
* renders the "needs setup" card in place of the story.
|
|
24
|
+
*/
|
|
25
|
+
declare const ONLOOK_CONTAINED_EVENT = "onlook/contained";
|
|
26
|
+
/**
|
|
27
|
+
* Emitted once per story mount, after the boundary's children committed
|
|
28
|
+
* without throwing. The verifier must key `rendered` on THIS (plus absence of
|
|
29
|
+
* Storybook error events) — never on `STORY_RENDERED` alone, which Storybook
|
|
30
|
+
* fires even after its own boundary catches.
|
|
31
|
+
*/
|
|
32
|
+
declare const ONLOOK_RENDERED_EVENT = "onlook/rendered";
|
|
33
|
+
/**
|
|
34
|
+
* Tag-for-verifier re-emit of errors the boundary cannot catch (loader /
|
|
35
|
+
* `beforeEach` / module-load throws surface as Storybook's
|
|
36
|
+
* `storyThrewException` / `storyErrored` channel events, outside the React
|
|
37
|
+
* render). These remain overlay-not-card; no DOM repaint is attempted.
|
|
38
|
+
*/
|
|
39
|
+
declare const ONLOOK_UNCONTAINED_ERROR_EVENT = "onlook/uncontained-error";
|
|
40
|
+
type OnlookContainedPayload = {
|
|
41
|
+
storyId: string;
|
|
42
|
+
contractVersion: number;
|
|
43
|
+
/** Trimmed failure summary (error message). */
|
|
44
|
+
message: string;
|
|
45
|
+
/** Present when the failure is attributed to a contained module (U4). */
|
|
46
|
+
module?: ContainedModuleMarker;
|
|
47
|
+
};
|
|
48
|
+
type OnlookRenderedPayload = {
|
|
49
|
+
storyId: string;
|
|
50
|
+
contractVersion: number;
|
|
51
|
+
};
|
|
52
|
+
type OnlookUncontainedErrorPayload = {
|
|
53
|
+
/** Best-effort (read from the preview iframe URL); absent when unknown. */
|
|
54
|
+
storyId?: string;
|
|
55
|
+
contractVersion: number;
|
|
56
|
+
message: string;
|
|
57
|
+
/** Which Storybook channel event this re-emits. */
|
|
58
|
+
source: 'storyThrewException' | 'storyErrored';
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Render-state attribute, value `'rendered' | 'contained'`. Set on
|
|
62
|
+
* `#storybook-root` (falling back to `document.body` when absent) after each
|
|
63
|
+
* boundary commit, and ALSO carried on the card's own wrapper element when
|
|
64
|
+
* contained. Cleared on boundary unmount (story switch), so a stale value
|
|
65
|
+
* never leaks into the next story's mount — verifiers should match it
|
|
66
|
+
* together with `data-onlook-story-id`.
|
|
67
|
+
*/
|
|
68
|
+
declare const ONLOOK_RENDER_STATE_ATTR = "data-onlook-render-state";
|
|
69
|
+
/** Companion attribute carrying the story id the render state belongs to. */
|
|
70
|
+
declare const ONLOOK_RENDER_STORY_ATTR = "data-onlook-story-id";
|
|
71
|
+
/** Present (empty value) on the "needs setup" card's wrapper element. */
|
|
72
|
+
declare const ONLOOK_CONTAINED_CARD_ATTR = "data-onlook-contained-card";
|
|
73
|
+
type OnlookRenderState = 'rendered' | 'contained';
|
|
74
|
+
|
|
75
|
+
type ContainmentCardProps = {
|
|
76
|
+
storyId: string;
|
|
77
|
+
storyLabel: string;
|
|
78
|
+
message: string;
|
|
79
|
+
module?: ContainedModuleMarker;
|
|
80
|
+
};
|
|
81
|
+
declare function ContainmentCard(props: ContainmentCardProps): React.ReactElement;
|
|
82
|
+
type BoundaryProps = {
|
|
83
|
+
storyId: string;
|
|
84
|
+
storyLabel: string;
|
|
85
|
+
children?: React.ReactNode;
|
|
86
|
+
};
|
|
87
|
+
type BoundaryState = {
|
|
88
|
+
error: unknown;
|
|
89
|
+
hasError: boolean;
|
|
90
|
+
};
|
|
91
|
+
declare class OnlookContainmentBoundary extends React.Component<BoundaryProps, BoundaryState> {
|
|
92
|
+
private renderedSignalSent;
|
|
93
|
+
constructor(props: BoundaryProps);
|
|
94
|
+
static getDerivedStateFromError(error: unknown): BoundaryState;
|
|
95
|
+
componentDidCatch(error: unknown): void;
|
|
96
|
+
componentDidMount(): void;
|
|
97
|
+
componentDidUpdate(): void;
|
|
98
|
+
componentWillUnmount(): void;
|
|
99
|
+
private signalRenderedIfClean;
|
|
100
|
+
render(): React.ReactNode;
|
|
101
|
+
}
|
|
102
|
+
type StoryContextLike = {
|
|
103
|
+
id?: string;
|
|
104
|
+
title?: string;
|
|
105
|
+
name?: string;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The project-level decorator delivered through the addon `./preview` entry.
|
|
109
|
+
* Keyed by story id so switching stories remounts the boundary (resetting
|
|
110
|
+
* caught-error state and re-arming the once-per-mount rendered signal).
|
|
111
|
+
*/
|
|
112
|
+
declare function onlookContainmentDecorator(Story: unknown, context?: StoryContextLike): React.ReactElement;
|
|
113
|
+
|
|
114
|
+
type ChannelLike = {
|
|
115
|
+
emit: (event: string, payload?: unknown) => void;
|
|
116
|
+
on?: (event: string, listener: (payload?: unknown) => void) => void;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The preview channel. Prefers `window.__STORYBOOK_ADDONS_CHANNEL__` (set by
|
|
120
|
+
* builder-vite's setup code before any addon preview annotation loads), then
|
|
121
|
+
* the global-backed preview addon store — but only when it already HAS a
|
|
122
|
+
* channel (calling `getChannel()` earlier would install a mock and clobber
|
|
123
|
+
* Storybook's own setup). `null` when Storybook hasn't wired one (e.g. tests,
|
|
124
|
+
* portable stories).
|
|
125
|
+
*/
|
|
126
|
+
declare function getPreviewChannel(): ChannelLike | null;
|
|
127
|
+
/**
|
|
128
|
+
* Snapshot tooling detection (Chromatic's documented `isChromatic()` check).
|
|
129
|
+
* The vite-plugin half of this package self-disables under `process.env.CI ||
|
|
130
|
+
* CHROMATIC`; the preview half can't see those env vars in the browser, so it
|
|
131
|
+
* keys on Chromatic's user agent / URL marker. When true we keep the boundary
|
|
132
|
+
* behavior (the card still renders deterministically) but skip the dynamic
|
|
133
|
+
* signals — channel events and root-element attribute writes — so snapshots
|
|
134
|
+
* stay byte-stable.
|
|
135
|
+
*/
|
|
136
|
+
declare function isSnapshotEnvironment(): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Attribute an error to a contained module (U4's env-containment stand-ins).
|
|
139
|
+
* Primary: the marker the stand-in attaches at `error.__onlookContained`.
|
|
140
|
+
* Fallback: scan the `globalThis.__ONLOOK_CONTAINED_MODULES__` registry for a
|
|
141
|
+
* marker whose modulePath appears in the error message (covers errors
|
|
142
|
+
* re-wrapped by customer code that drops custom properties).
|
|
143
|
+
*/
|
|
144
|
+
declare function attributeErrorToModule(error: unknown): ContainedModuleMarker | undefined;
|
|
145
|
+
declare function installUncontainedErrorRelay(channel?: ChannelLike | null): void;
|
|
146
|
+
/**
|
|
147
|
+
* The preview annotations' `beforeAll` — runs once when the preview boots,
|
|
148
|
+
* before any story (and before any loader can throw). Advertises the
|
|
149
|
+
* handshake global and installs the uncontained-error relay.
|
|
150
|
+
*/
|
|
151
|
+
declare function onlookBeforeAll(): void;
|
|
152
|
+
|
|
153
|
+
declare const decorators: (typeof onlookContainmentDecorator)[];
|
|
154
|
+
declare const beforeAll: typeof onlookBeforeAll;
|
|
155
|
+
|
|
156
|
+
export { ContainmentCard, ONLOOK_CONTAINED_CARD_ATTR, ONLOOK_CONTAINED_EVENT, ONLOOK_CONTAINMENT_CONTRACT_GLOBAL, ONLOOK_CONTAINMENT_CONTRACT_VERSION, ONLOOK_RENDERED_EVENT, ONLOOK_RENDER_STATE_ATTR, ONLOOK_RENDER_STORY_ATTR, ONLOOK_UNCONTAINED_ERROR_EVENT, type OnlookContainedPayload, OnlookContainmentBoundary, type OnlookContainmentContract, type OnlookRenderState, type OnlookRenderedPayload, type OnlookUncontainedErrorPayload, attributeErrorToModule, beforeAll, decorators, getPreviewChannel, installUncontainedErrorRelay, isSnapshotEnvironment, onlookBeforeAll, onlookContainmentDecorator };
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
// src/preview/containment-boundary.ts
|
|
4
|
+
|
|
5
|
+
// src/containment-contract/containment-contract.ts
|
|
6
|
+
var ONLOOK_CONTAINMENT_CONTRACT_VERSION = 1;
|
|
7
|
+
var ONLOOK_CONTAINED_MODULES_GLOBAL = "__ONLOOK_CONTAINED_MODULES__";
|
|
8
|
+
var ONLOOK_CONTAINED_PROP = "__onlookContained";
|
|
9
|
+
var ONLOOK_CONTAINMENT_CONTRACT_GLOBAL = "__ONLOOK_CONTAINMENT_CONTRACT__";
|
|
10
|
+
var ONLOOK_CONTAINED_EVENT = "onlook/contained";
|
|
11
|
+
var ONLOOK_RENDERED_EVENT = "onlook/rendered";
|
|
12
|
+
var ONLOOK_UNCONTAINED_ERROR_EVENT = "onlook/uncontained-error";
|
|
13
|
+
var ONLOOK_RENDER_STATE_ATTR = "data-onlook-render-state";
|
|
14
|
+
var ONLOOK_RENDER_STORY_ATTR = "data-onlook-story-id";
|
|
15
|
+
var ONLOOK_CONTAINED_CARD_ATTR = "data-onlook-contained-card";
|
|
16
|
+
|
|
17
|
+
// src/preview/signals.ts
|
|
18
|
+
var STORYBOOK_STORY_THREW_EXCEPTION = "storyThrewException";
|
|
19
|
+
var STORYBOOK_STORY_ERRORED = "storyErrored";
|
|
20
|
+
function globalScope() {
|
|
21
|
+
return globalThis;
|
|
22
|
+
}
|
|
23
|
+
function getPreviewChannel() {
|
|
24
|
+
const g = globalScope();
|
|
25
|
+
const direct = g.__STORYBOOK_ADDONS_CHANNEL__;
|
|
26
|
+
if (direct && typeof direct.emit === "function") return direct;
|
|
27
|
+
const store = g.__STORYBOOK_ADDONS_PREVIEW;
|
|
28
|
+
if (store && typeof store.hasChannel === "function" && store.hasChannel() && typeof store.getChannel === "function") {
|
|
29
|
+
return store.getChannel();
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function isSnapshotEnvironment() {
|
|
34
|
+
try {
|
|
35
|
+
const w = globalScope().window;
|
|
36
|
+
if (!w) return false;
|
|
37
|
+
if (/Chromatic/.test(w.navigator?.userAgent ?? "")) return true;
|
|
38
|
+
if (/chromatic=true/.test(w.location?.href ?? "")) return true;
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function exposeContract() {
|
|
44
|
+
globalScope()[ONLOOK_CONTAINMENT_CONTRACT_GLOBAL] = {
|
|
45
|
+
version: ONLOOK_CONTAINMENT_CONTRACT_VERSION
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function signalElement() {
|
|
49
|
+
if (typeof document === "undefined") return null;
|
|
50
|
+
return document.getElementById("storybook-root") ?? document.body ?? null;
|
|
51
|
+
}
|
|
52
|
+
function setRenderState(state, storyId) {
|
|
53
|
+
if (isSnapshotEnvironment()) return;
|
|
54
|
+
try {
|
|
55
|
+
const el = signalElement();
|
|
56
|
+
if (!el) return;
|
|
57
|
+
el.setAttribute(ONLOOK_RENDER_STATE_ATTR, state);
|
|
58
|
+
el.setAttribute(ONLOOK_RENDER_STORY_ATTR, storyId);
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function clearRenderState(storyId) {
|
|
63
|
+
try {
|
|
64
|
+
const el = signalElement();
|
|
65
|
+
if (!el) return;
|
|
66
|
+
if (el.getAttribute(ONLOOK_RENDER_STORY_ATTR) !== storyId) return;
|
|
67
|
+
el.removeAttribute(ONLOOK_RENDER_STATE_ATTR);
|
|
68
|
+
el.removeAttribute(ONLOOK_RENDER_STORY_ATTR);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function emit(event, payload) {
|
|
73
|
+
if (isSnapshotEnvironment()) return;
|
|
74
|
+
try {
|
|
75
|
+
getPreviewChannel()?.emit(event, payload);
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function emitContained(payload) {
|
|
80
|
+
emit(ONLOOK_CONTAINED_EVENT, payload);
|
|
81
|
+
}
|
|
82
|
+
function emitRendered(storyId) {
|
|
83
|
+
emit(ONLOOK_RENDERED_EVENT, {
|
|
84
|
+
storyId,
|
|
85
|
+
contractVersion: ONLOOK_CONTAINMENT_CONTRACT_VERSION
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function summarizeError(error, maxLength = 400) {
|
|
89
|
+
const raw = error instanceof Error ? error.message : typeof error === "string" ? error : String(error);
|
|
90
|
+
const oneline = raw.trim();
|
|
91
|
+
return oneline.length > maxLength ? `${oneline.slice(0, maxLength - 1)}\u2026` : oneline;
|
|
92
|
+
}
|
|
93
|
+
function attributeErrorToModule(error) {
|
|
94
|
+
if (error && typeof error === "object") {
|
|
95
|
+
const direct = error[ONLOOK_CONTAINED_PROP];
|
|
96
|
+
if (isMarker(direct)) return direct;
|
|
97
|
+
}
|
|
98
|
+
const registry = globalScope()[ONLOOK_CONTAINED_MODULES_GLOBAL];
|
|
99
|
+
if (!registry) return void 0;
|
|
100
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
101
|
+
if (!message) return void 0;
|
|
102
|
+
for (const candidate of Object.values(registry)) {
|
|
103
|
+
if (isMarker(candidate) && message.includes(candidate.modulePath)) {
|
|
104
|
+
return candidate;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
function isMarker(value) {
|
|
110
|
+
return value !== null && typeof value === "object" && typeof value.modulePath === "string" && value.reason === "env-validation";
|
|
111
|
+
}
|
|
112
|
+
var INSTALLED_CHANNELS = /* @__PURE__ */ new WeakSet();
|
|
113
|
+
function currentStoryId() {
|
|
114
|
+
try {
|
|
115
|
+
const w = globalScope().window;
|
|
116
|
+
const search = w?.location?.search;
|
|
117
|
+
if (!search) return void 0;
|
|
118
|
+
return new URLSearchParams(search).get("id") ?? void 0;
|
|
119
|
+
} catch {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function relayPayload(source, raw) {
|
|
124
|
+
const obj = raw ?? {};
|
|
125
|
+
const message = typeof obj.message === "string" ? obj.message : typeof obj.description === "string" ? obj.description : typeof obj.title === "string" ? obj.title : summarizeError(raw);
|
|
126
|
+
const storyId = currentStoryId();
|
|
127
|
+
return {
|
|
128
|
+
...storyId ? { storyId } : {},
|
|
129
|
+
contractVersion: ONLOOK_CONTAINMENT_CONTRACT_VERSION,
|
|
130
|
+
message: summarizeError(message),
|
|
131
|
+
source
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function installUncontainedErrorRelay(channel = getPreviewChannel()) {
|
|
135
|
+
if (!channel || typeof channel.on !== "function") return;
|
|
136
|
+
if (INSTALLED_CHANNELS.has(channel)) return;
|
|
137
|
+
INSTALLED_CHANNELS.add(channel);
|
|
138
|
+
channel.on(STORYBOOK_STORY_THREW_EXCEPTION, (payload) => {
|
|
139
|
+
emit(ONLOOK_UNCONTAINED_ERROR_EVENT, relayPayload("storyThrewException", payload));
|
|
140
|
+
});
|
|
141
|
+
channel.on(STORYBOOK_STORY_ERRORED, (payload) => {
|
|
142
|
+
emit(ONLOOK_UNCONTAINED_ERROR_EVENT, relayPayload("storyErrored", payload));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function onlookBeforeAll() {
|
|
146
|
+
exposeContract();
|
|
147
|
+
installUncontainedErrorRelay();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/preview/containment-boundary.ts
|
|
151
|
+
var cardStyles = {
|
|
152
|
+
wrapper: {
|
|
153
|
+
boxSizing: "border-box",
|
|
154
|
+
fontFamily: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
|
|
155
|
+
color: "#1f2937",
|
|
156
|
+
background: "#fafaf9",
|
|
157
|
+
border: "1px solid #e7e5e4",
|
|
158
|
+
borderRadius: "12px",
|
|
159
|
+
padding: "20px 24px",
|
|
160
|
+
margin: "16px",
|
|
161
|
+
maxWidth: "560px",
|
|
162
|
+
lineHeight: 1.5
|
|
163
|
+
},
|
|
164
|
+
badge: {
|
|
165
|
+
display: "inline-block",
|
|
166
|
+
fontSize: "11px",
|
|
167
|
+
fontWeight: 600,
|
|
168
|
+
letterSpacing: "0.04em",
|
|
169
|
+
textTransform: "uppercase",
|
|
170
|
+
color: "#92400e",
|
|
171
|
+
background: "#fef3c7",
|
|
172
|
+
borderRadius: "999px",
|
|
173
|
+
padding: "2px 10px",
|
|
174
|
+
marginBottom: "10px"
|
|
175
|
+
},
|
|
176
|
+
title: {
|
|
177
|
+
fontSize: "15px",
|
|
178
|
+
fontWeight: 600,
|
|
179
|
+
margin: "0 0 6px"
|
|
180
|
+
},
|
|
181
|
+
detail: {
|
|
182
|
+
fontSize: "13px",
|
|
183
|
+
margin: "0 0 10px",
|
|
184
|
+
color: "#44403c"
|
|
185
|
+
},
|
|
186
|
+
message: {
|
|
187
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
188
|
+
fontSize: "12px",
|
|
189
|
+
whiteSpace: "pre-wrap",
|
|
190
|
+
wordBreak: "break-word",
|
|
191
|
+
color: "#7f1d1d",
|
|
192
|
+
background: "#fef2f2",
|
|
193
|
+
border: "1px solid #fee2e2",
|
|
194
|
+
borderRadius: "8px",
|
|
195
|
+
padding: "10px 12px",
|
|
196
|
+
margin: "0 0 10px"
|
|
197
|
+
},
|
|
198
|
+
footer: {
|
|
199
|
+
fontSize: "12px",
|
|
200
|
+
margin: 0,
|
|
201
|
+
color: "#78716c"
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var h = React.createElement;
|
|
205
|
+
function ContainmentCard(props) {
|
|
206
|
+
const { storyId, storyLabel, message, module } = props;
|
|
207
|
+
const moduleDetail = module ? h(
|
|
208
|
+
"p",
|
|
209
|
+
{ style: cardStyles.detail, key: "module" },
|
|
210
|
+
`This story imports `,
|
|
211
|
+
h("code", null, module.modulePath),
|
|
212
|
+
module.envKeys.length > 0 ? `, which validates environment variables at load time. Missing: ${module.envKeys.join(", ")}.` : ", which could not load in this environment."
|
|
213
|
+
) : null;
|
|
214
|
+
return h(
|
|
215
|
+
"div",
|
|
216
|
+
{
|
|
217
|
+
[ONLOOK_CONTAINED_CARD_ATTR]: "",
|
|
218
|
+
[ONLOOK_RENDER_STATE_ATTR]: "contained",
|
|
219
|
+
[ONLOOK_RENDER_STORY_ATTR]: storyId,
|
|
220
|
+
role: "alert",
|
|
221
|
+
style: cardStyles.wrapper
|
|
222
|
+
},
|
|
223
|
+
h("span", { style: cardStyles.badge, key: "badge" }, "Needs setup"),
|
|
224
|
+
h("h2", { style: cardStyles.title, key: "title" }, `${storyLabel} can't render yet`),
|
|
225
|
+
moduleDetail,
|
|
226
|
+
message ? h("pre", { style: cardStyles.message, key: "message" }, message) : null,
|
|
227
|
+
h(
|
|
228
|
+
"p",
|
|
229
|
+
{ style: cardStyles.footer, key: "footer" },
|
|
230
|
+
"Onlook contained this error so the rest of Storybook keeps working."
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
var OnlookContainmentBoundary = class extends React.Component {
|
|
235
|
+
// `onlook/rendered` fires once per story mount — re-renders of a healthy
|
|
236
|
+
// story (arg updates) don't re-emit.
|
|
237
|
+
renderedSignalSent = false;
|
|
238
|
+
constructor(props) {
|
|
239
|
+
super(props);
|
|
240
|
+
this.state = { error: null, hasError: false };
|
|
241
|
+
}
|
|
242
|
+
static getDerivedStateFromError(error) {
|
|
243
|
+
return { error, hasError: true };
|
|
244
|
+
}
|
|
245
|
+
componentDidCatch(error) {
|
|
246
|
+
const module = attributeErrorToModule(error);
|
|
247
|
+
setRenderState("contained", this.props.storyId);
|
|
248
|
+
emitContained({
|
|
249
|
+
storyId: this.props.storyId,
|
|
250
|
+
contractVersion: ONLOOK_CONTAINMENT_CONTRACT_VERSION,
|
|
251
|
+
message: summarizeError(error),
|
|
252
|
+
...module ? { module } : {}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
componentDidMount() {
|
|
256
|
+
exposeContract();
|
|
257
|
+
this.signalRenderedIfClean();
|
|
258
|
+
}
|
|
259
|
+
componentDidUpdate() {
|
|
260
|
+
this.signalRenderedIfClean();
|
|
261
|
+
}
|
|
262
|
+
componentWillUnmount() {
|
|
263
|
+
clearRenderState(this.props.storyId);
|
|
264
|
+
}
|
|
265
|
+
signalRenderedIfClean() {
|
|
266
|
+
if (this.state.hasError || this.renderedSignalSent) return;
|
|
267
|
+
this.renderedSignalSent = true;
|
|
268
|
+
setRenderState("rendered", this.props.storyId);
|
|
269
|
+
emitRendered(this.props.storyId);
|
|
270
|
+
}
|
|
271
|
+
render() {
|
|
272
|
+
if (this.state.hasError) {
|
|
273
|
+
return h(ContainmentCard, {
|
|
274
|
+
storyId: this.props.storyId,
|
|
275
|
+
storyLabel: this.props.storyLabel,
|
|
276
|
+
message: summarizeError(this.state.error),
|
|
277
|
+
module: attributeErrorToModule(this.state.error)
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return this.props.children;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
function storyLabelFrom(context) {
|
|
284
|
+
const title = context.title?.split("/").pop();
|
|
285
|
+
if (title && context.name) return `${title} \xB7 ${context.name}`;
|
|
286
|
+
return title ?? context.name ?? context.id ?? "This story";
|
|
287
|
+
}
|
|
288
|
+
function onlookContainmentDecorator(Story, context) {
|
|
289
|
+
const ctx = context ?? {};
|
|
290
|
+
const storyId = ctx.id ?? "unknown";
|
|
291
|
+
return h(
|
|
292
|
+
OnlookContainmentBoundary,
|
|
293
|
+
{ key: storyId, storyId, storyLabel: storyLabelFrom(ctx) },
|
|
294
|
+
h(Story)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/preview/index.ts
|
|
299
|
+
var decorators = [onlookContainmentDecorator];
|
|
300
|
+
var beforeAll = onlookBeforeAll;
|
|
301
|
+
|
|
302
|
+
export { ContainmentCard, ONLOOK_CONTAINED_CARD_ATTR, ONLOOK_CONTAINED_EVENT, ONLOOK_CONTAINMENT_CONTRACT_GLOBAL, ONLOOK_CONTAINMENT_CONTRACT_VERSION, ONLOOK_RENDERED_EVENT, ONLOOK_RENDER_STATE_ATTR, ONLOOK_RENDER_STORY_ATTR, ONLOOK_UNCONTAINED_ERROR_EVENT, OnlookContainmentBoundary, attributeErrorToModule, beforeAll, decorators, getPreviewChannel, installUncontainedErrorRelay, isSnapshotEnvironment, onlookBeforeAll, onlookContainmentDecorator };
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
export { closeBrowser } from './utils/browser/index.js';
|
|
2
2
|
import 'playwright';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Generate screenshots for all stories (parallelized for speed)
|
|
6
|
-
*
|
|
7
|
-
* @param offset - Absolute index offset for logging (e.g. skip=200 → offset=200)
|
|
8
|
-
* @param total - Total number of stories across all runs (for [n/total] logging)
|
|
9
|
-
*/
|
|
10
4
|
declare function generateAllScreenshots(stories: Array<{
|
|
11
5
|
id: string;
|
|
12
6
|
importPath: string;
|