@forgebook/react 0.1.8 → 0.2.1

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.
Files changed (58) hide show
  1. package/README.md +5 -1
  2. package/dist/src/StoryErrorBoundary.d.ts +0 -1
  3. package/dist/src/StoryErrorBoundary.js +0 -1
  4. package/dist/src/clock.d.ts +0 -1
  5. package/dist/src/clock.js +0 -1
  6. package/dist/src/mountFrame.d.ts +0 -1
  7. package/dist/src/mountFrame.js +2 -3
  8. package/dist/src/reactFrameRenderer.d.ts +16 -0
  9. package/dist/src/reactFrameRenderer.js +43 -0
  10. package/dist/src/reactRenderer.d.ts +5 -7
  11. package/dist/src/reactRenderer.js +15 -71
  12. package/dist/src/stories.d.ts +0 -1
  13. package/dist/src/stories.js +0 -1
  14. package/dist/src/storyElement.d.ts +17 -2
  15. package/dist/src/storyElement.js +64 -7
  16. package/dist/src/storyJsx.d.ts +1 -16
  17. package/dist/src/storyJsx.js +5 -41
  18. package/dist/src/storyProviders.d.ts +15 -0
  19. package/dist/src/storyProviders.js +11 -0
  20. package/dist/src/storybookHooks.d.ts +74 -0
  21. package/dist/src/storybookHooks.js +82 -0
  22. package/dist/src/texts.d.ts +0 -1
  23. package/dist/src/texts.js +0 -1
  24. package/dist/src/vite/extractDocs.d.ts +13 -18
  25. package/dist/src/vite/extractDocs.js +6 -496
  26. package/dist/src/vite/react.d.ts +6 -3
  27. package/dist/src/vite/react.js +15 -7
  28. package/dist/src/vite/refreshAtOnce.d.ts +25 -0
  29. package/dist/src/vite/refreshAtOnce.js +40 -0
  30. package/dist/src/vite/refreshThroughCasts.d.ts +19 -0
  31. package/dist/src/vite/refreshThroughCasts.js +26 -0
  32. package/dist/src/vite/storybookPreviewApi.d.ts +14 -0
  33. package/dist/src/vite/storybookPreviewApi.js +94 -0
  34. package/package.json +13 -5
  35. package/dist/src/StoryErrorBoundary.d.ts.map +0 -1
  36. package/dist/src/StoryErrorBoundary.js.map +0 -1
  37. package/dist/src/clock.d.ts.map +0 -1
  38. package/dist/src/clock.js.map +0 -1
  39. package/dist/src/mountFrame.d.ts.map +0 -1
  40. package/dist/src/mountFrame.js.map +0 -1
  41. package/dist/src/reactRenderer.d.ts.map +0 -1
  42. package/dist/src/reactRenderer.js.map +0 -1
  43. package/dist/src/stories.d.ts.map +0 -1
  44. package/dist/src/stories.js.map +0 -1
  45. package/dist/src/storyElement.d.ts.map +0 -1
  46. package/dist/src/storyElement.js.map +0 -1
  47. package/dist/src/storyJsx.d.ts.map +0 -1
  48. package/dist/src/storyJsx.js.map +0 -1
  49. package/dist/src/texts.d.ts.map +0 -1
  50. package/dist/src/texts.js.map +0 -1
  51. package/dist/src/vite/extractDocs.d.ts.map +0 -1
  52. package/dist/src/vite/extractDocs.js.map +0 -1
  53. package/dist/src/vite/extractSignature.d.ts +0 -19
  54. package/dist/src/vite/extractSignature.d.ts.map +0 -1
  55. package/dist/src/vite/extractSignature.js +0 -59
  56. package/dist/src/vite/extractSignature.js.map +0 -1
  57. package/dist/src/vite/react.d.ts.map +0 -1
  58. package/dist/src/vite/react.js.map +0 -1
package/README.md CHANGED
@@ -19,6 +19,10 @@ import { defineConfig } from "forgebook";
19
19
  export default defineConfig({ framework: react(), title: "My components", stories: ["src/**/*.stories.tsx"] });
20
20
  ```
21
21
 
22
+ `react()` brings React's Vite plugin, with Fast Refresh; a library whose files need it configured passes its options,
23
+ as `react({ include: /\.[jt]sx?$/ })` for JSX in `.js` files. Leave the plugin to the adapter rather than adding your own:
24
+ the adapter's plugins around it redraw an edited component sooner.
25
+
22
26
  and the stories take their types from it:
23
27
 
24
28
  ```tsx
@@ -34,7 +38,7 @@ export const Default: Story<typeof Badge> = { args: { label: "New" } };
34
38
  | Import | What it is |
35
39
  |---|---|
36
40
  | `@forgebook/react` | `Meta`, `Story`, `Decorator`, `Preview`: CSF 3 typed for React, the args from the component's props |
37
- | `@forgebook/react/vite` | `react()`, the framework adapter of `forgebook.config.ts` |
41
+ | `@forgebook/react/vite` | `react()`, the framework adapter of `forgebook.config.ts`; it takes `@vitejs/plugin-react`'s options, as `react({ include: /\.[jt]sx?$/ })` to give Fast Refresh to `.js` files |
38
42
  | `@forgebook/react/texts`, `@forgebook/react/clock` | A story's texts in the catalogue's language, and the story clock, as hooks |
39
43
 
40
44
  The rest of its exports are what the catalogue loads itself. Getting started, the catalogue's features and its
@@ -56,4 +56,3 @@ export declare class StoryErrorBoundary extends Component<StoryErrorBoundaryProp
56
56
  render(): ReactNode;
57
57
  }
58
58
  export {};
59
- //# sourceMappingURL=StoryErrorBoundary.d.ts.map
@@ -56,4 +56,3 @@ export class StoryErrorBoundary extends Component {
56
56
  return this.state.hasError ? null : this.props.children;
57
57
  }
58
58
  }
59
- //# sourceMappingURL=StoryErrorBoundary.js.map
@@ -23,4 +23,3 @@ export declare const useStoryClock: () => StoryClock;
23
23
  * @returns the moment, in milliseconds since the Unix epoch.
24
24
  */
25
25
  export declare const useStoryNow: () => number;
26
- //# sourceMappingURL=clock.d.ts.map
package/dist/src/clock.js CHANGED
@@ -27,4 +27,3 @@ export const useStoryNow = () => {
27
27
  const clock = useStoryClock();
28
28
  return useSyncExternalStore(clock.subscribe, clock.now, clock.now);
29
29
  };
30
- //# sourceMappingURL=clock.js.map
@@ -12,4 +12,3 @@ import { type FrameConfig } from "forgebook/frame";
12
12
  * @param config - the sources and the languages, as the shell's configuration gives them.
13
13
  */
14
14
  export declare const mountFrame: (container: HTMLElement, config: Omit<FrameConfig, "renderer">) => Promise<void>;
15
- //# sourceMappingURL=mountFrame.d.ts.map
@@ -5,12 +5,11 @@
5
5
  * @module
6
6
  */
7
7
  import { mountFrame as mountForgebookFrame } from "forgebook/frame";
8
- import { reactRenderer } from "./reactRenderer.js";
8
+ import { reactFrameRenderer } from "./reactFrameRenderer.js";
9
9
  /**
10
10
  * Shows the story the address names in the given element, drawn with React.
11
11
  *
12
12
  * @param container - the element the story renders into.
13
13
  * @param config - the sources and the languages, as the shell's configuration gives them.
14
14
  */
15
- export const mountFrame = (container, config) => mountForgebookFrame(container, { ...config, renderer: reactRenderer });
16
- //# sourceMappingURL=mountFrame.js.map
15
+ export const mountFrame = (container, config) => mountForgebookFrame(container, { ...config, renderer: reactFrameRenderer });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Forgebook's renderer for React, as the frame draws with it: a story in a canvas with a React root of its own, inside
3
+ * the texts' and the clock's providers and the catalogue's decorators, catching what the story throws.
4
+ *
5
+ * @remarks
6
+ * The frame runs everything else the same for every framework (`forgebook/renderer`): the play function, Replay, the
7
+ * matrix, the error shown in place of the story. Drawing again with other args keeps the story's state; the frame
8
+ * mounts it anew when a story must start over. The server render is in `reactRenderer.tsx` and nothing here imports
9
+ * it, so React's server renderer never reaches a frame page: neither a static catalogue's files nor a first start's
10
+ * pre-bundle.
11
+ *
12
+ * @module
13
+ */
14
+ import type { FrameRenderer } from "forgebook/renderer";
15
+ /** React as the frame draws with it. */
16
+ export declare const reactFrameRenderer: FrameRenderer;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { storyContextOf } from "forgebook/renderer";
3
+ import { useEffect, useRef } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+ import { StoryErrorBoundary } from "./StoryErrorBoundary.js";
6
+ import { storyElement } from "./storyElement.js";
7
+ import { inProviders } from "./storyProviders.js";
8
+ /**
9
+ * The story, telling once that it is drawn: its effect runs after React put it in the page, and does not run when the
10
+ * story threw.
11
+ *
12
+ * @param props - the story's element and what to call once it is drawn.
13
+ */
14
+ const Drawn = ({ children, onDrawn }) => {
15
+ const drawnRef = useRef(onDrawn);
16
+ useEffect(() => {
17
+ drawnRef.current();
18
+ }, []);
19
+ return children;
20
+ };
21
+ /** React as the frame draws with it. */
22
+ export const reactFrameRenderer = {
23
+ mount: (canvas, first, onError) => {
24
+ const root = createRoot(canvas);
25
+ let markDrawn = () => undefined;
26
+ const drawn = new Promise((resolve) => {
27
+ markDrawn = resolve;
28
+ });
29
+ let drawing = first;
30
+ const draw = () => {
31
+ root.render(inProviders(drawing, _jsx(StoryErrorBoundary, { resetKey: drawing.args, onError: onError, children: _jsx(Drawn, { onDrawn: markDrawn, children: storyElement(drawing.story, storyContextOf(drawing, drawing.args, () => canvas), drawing.decorators, drawing.changeArgs) }) })));
32
+ };
33
+ draw();
34
+ return {
35
+ drawn,
36
+ update: (next) => {
37
+ drawing = next;
38
+ draw();
39
+ },
40
+ unmount: () => root.unmount()
41
+ };
42
+ }
43
+ };
@@ -1,15 +1,13 @@
1
1
  /**
2
- * Forgebook's renderer for React: draws a story in a canvas with a React root of its own, inside the texts' and the
3
- * clock's providers and the catalogue's decorators, and catches what the story throws.
2
+ * Forgebook's renderer for React, whole: the frame's drawing (`reactFrameRenderer.tsx`) and the story drawn as HTML
3
+ * on the server, for the generated server tests.
4
4
  *
5
5
  * @remarks
6
- * The frame runs everything else the same for every framework (`forgebook/renderer`): the play function, Replay, the
7
- * matrix, the error shown in place of the story. Drawing again with other args keeps the story's state; the frame
8
- * mounts it anew when a story must start over.
6
+ * The frame page imports `reactFrameRenderer.tsx` alone, so React's server renderer never reaches it. Here it still
7
+ * loads only when a server test renders a story (`renderToString`), so a test that only mounts does not load it.
9
8
  *
10
9
  * @module
11
10
  */
12
11
  import type { StoryRenderer } from "forgebook/renderer";
13
- /** React as the frame draws with it. */
12
+ /** React as the frame draws with it and the server tests render with it. */
14
13
  export declare const reactRenderer: StoryRenderer;
15
- //# sourceMappingURL=reactRenderer.d.ts.map
@@ -1,79 +1,23 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { storyTextOf } from "@forgebook/story-format/texts";
3
- import { untracedStep } from "forgebook/renderer";
4
- import { useEffect, useRef } from "react";
5
- import { createRoot } from "react-dom/client";
6
- import { renderToString } from "react-dom/server";
7
- import { StoryClockContext } from "./clock.js";
8
- import { StoryErrorBoundary } from "./StoryErrorBoundary.js";
9
- import { storyElement } from "./storyElement.js";
10
- import { StoryTextsContext } from "./texts.js";
11
1
  /**
12
- * The story in the texts' and the clock's providers; the language's provider is a decorator of the catalogue's
13
- * preview, inside these.
2
+ * Forgebook's renderer for React, whole: the frame's drawing (`reactFrameRenderer.tsx`) and the story drawn as HTML
3
+ * on the server, for the generated server tests.
14
4
  *
15
- * @param drawing - the story's texts and clock.
16
- * @param story - the story's element, to wrap.
17
- */
18
- const inProviders = (drawing, story) => (_jsx(StoryTextsContext.Provider, { value: drawing.texts, children: _jsx(StoryClockContext.Provider, { value: drawing.clock, children: story }) }));
19
- /**
20
- * What a render and a decorator receive.
5
+ * @remarks
6
+ * The frame page imports `reactFrameRenderer.tsx` alone, so React's server renderer never reaches it. Here it still
7
+ * loads only when a server test renders a story (`renderToString`), so a test that only mounts does not load it.
21
8
  *
22
- * @param drawing - the story's args, texts, clock and switches' values.
23
- * @param canvasOf - the element the story is drawn in, read only when a story asks for it: on the server there is
24
- * none, and a story that reads it there fails as it would in a page rendered on the server.
9
+ * @module
25
10
  */
26
- const contextOf = (drawing, canvasOf) => ({
27
- args: drawing.args,
28
- step: untracedStep,
29
- text: storyTextOf(drawing.texts),
30
- clock: drawing.clock,
31
- globals: drawing.globals,
32
- loaded: drawing.loaded,
33
- ...drawing.identity,
34
- parameters: drawing.story.parameters,
35
- get canvasElement() {
36
- return canvasOf();
37
- }
38
- });
39
- /**
40
- * The story, telling once that it is drawn: its effect runs after React put it in the page, and does not run when the
41
- * story threw.
42
- *
43
- * @param props - the story's element and what to call once it is drawn.
44
- */
45
- const Drawn = ({ children, onDrawn }) => {
46
- const drawnRef = useRef(onDrawn);
47
- useEffect(() => {
48
- drawnRef.current();
49
- }, []);
50
- return children;
51
- };
52
- /** React as the frame draws with it. */
11
+ import { storyContextOf } from "forgebook/renderer";
12
+ import { reactFrameRenderer } from "./reactFrameRenderer.js";
13
+ import { storyElement } from "./storyElement.js";
14
+ import { inProviders } from "./storyProviders.js";
15
+ /** React as the frame draws with it and the server tests render with it. */
53
16
  export const reactRenderer = {
54
- mount: (canvas, first, onError) => {
55
- const root = createRoot(canvas);
56
- let markDrawn = () => undefined;
57
- const drawn = new Promise((resolve) => {
58
- markDrawn = resolve;
59
- });
60
- let drawing = first;
61
- const draw = () => {
62
- root.render(inProviders(drawing, _jsx(StoryErrorBoundary, { resetKey: drawing.args, onError: onError, children: _jsx(Drawn, { onDrawn: markDrawn, children: storyElement(drawing.story, contextOf(drawing, () => canvas), drawing.decorators) }) })));
63
- };
64
- draw();
65
- return {
66
- drawn,
67
- update: (next) => {
68
- drawing = next;
69
- draw();
70
- },
71
- unmount: () => root.unmount()
72
- };
73
- },
74
- renderToString: (drawing) => {
75
- const context = contextOf(drawing, () => document.body);
17
+ ...reactFrameRenderer,
18
+ renderToString: async (drawing) => {
19
+ const { renderToString } = await import("react-dom/server");
20
+ const context = storyContextOf(drawing, drawing.args, () => document.body);
76
21
  return renderToString(inProviders(drawing, storyElement(drawing.story, context, drawing.decorators)));
77
22
  }
78
23
  };
79
- //# sourceMappingURL=reactRenderer.js.map
@@ -67,4 +67,3 @@ export type Decorator<TArgs> = CsfDecorator<ReactRenderer, TArgs>;
67
67
  */
68
68
  export type Preview = CsfPreview<ReactRenderer>;
69
69
  export {};
70
- //# sourceMappingURL=stories.d.ts.map
@@ -1 +0,0 @@
1
- //# sourceMappingURL=stories.js.map
@@ -3,9 +3,24 @@
3
3
  * the catalogue's (its preview's), in that order outward. The story's canvas and each cell of its matrix draw it the
4
4
  * same way.
5
5
  *
6
+ * @remarks
7
+ * The render and the decorators run inside components (`StoryDraw`, and the kept `storyAt` of each level), never while
8
+ * the element is built: Storybook's users write hooks in both, and a hook outside a render throws. The components are
9
+ * module-level functions, so new args draw the same components again and the story keeps its state; another story is
10
+ * another key, so it starts over. Since a decorator's story reads its level from a context, a decorator calls it while
11
+ * it renders, as it would a hook: not from an event handler, and not in another React root.
12
+ *
13
+ * What a decorator gets as its story is a component too, the same one for every drawing of a level (`storyAt`): a
14
+ * decorator writes `<Story />` as often as `story()`, and a new function at each drawing would be a new component to
15
+ * React, mounting the story afresh and losing its state whenever an arg changed.
16
+ *
17
+ * Each decorator and the render are called with the context Storybook's preview hooks read (`storybookHooks.ts`), so
18
+ * a story file written for Storybook (`useArgs` in a decorator) runs as it does there.
19
+ *
6
20
  * @module
7
21
  */
8
22
  import type { StoryContext } from "@forgebook/story-format/csf";
23
+ import type { ArgsChange } from "forgebook/renderer";
9
24
  import type { LoadedArgs, LoadedDecorator, LoadedStory } from "forgebook/story-module";
10
25
  import { type ReactNode } from "react";
11
26
  /**
@@ -14,6 +29,6 @@ import { type ReactNode } from "react";
14
29
  * @param loaded - the story.
15
30
  * @param context - the args and the canvas its render, play and decorators read.
16
31
  * @param outer - the catalogue's decorators, outside the story's and the meta's.
32
+ * @param changeArgs - changes the story's args from its own code (Storybook's `useArgs`); none changes nothing.
17
33
  */
18
- export declare const storyElement: (loaded: LoadedStory, context: StoryContext<LoadedArgs>, outer: readonly LoadedDecorator[]) => ReactNode;
19
- //# sourceMappingURL=storyElement.d.ts.map
34
+ export declare const storyElement: (loaded: LoadedStory, context: StoryContext<LoadedArgs>, outer: readonly LoadedDecorator[], changeArgs?: (change: ArgsChange) => void) => ReactNode;
@@ -3,9 +3,24 @@
3
3
  * the catalogue's (its preview's), in that order outward. The story's canvas and each cell of its matrix draw it the
4
4
  * same way.
5
5
  *
6
+ * @remarks
7
+ * The render and the decorators run inside components (`StoryDraw`, and the kept `storyAt` of each level), never while
8
+ * the element is built: Storybook's users write hooks in both, and a hook outside a render throws. The components are
9
+ * module-level functions, so new args draw the same components again and the story keeps its state; another story is
10
+ * another key, so it starts over. Since a decorator's story reads its level from a context, a decorator calls it while
11
+ * it renders, as it would a hook: not from an event handler, and not in another React root.
12
+ *
13
+ * What a decorator gets as its story is a component too, the same one for every drawing of a level (`storyAt`): a
14
+ * decorator writes `<Story />` as often as `story()`, and a new function at each drawing would be a new component to
15
+ * React, mounting the story afresh and losing its state whenever an arg changed.
16
+ *
17
+ * Each decorator and the render are called with the context Storybook's preview hooks read (`storybookHooks.ts`), so
18
+ * a story file written for Storybook (`useArgs` in a decorator) runs as it does there.
19
+ *
6
20
  * @module
7
21
  */
8
- import { createElement } from "react";
22
+ import { createContext, createElement, useContext } from "react";
23
+ import { withStoryHooks } from "./storybookHooks.js";
9
24
  /**
10
25
  * A loaded story read as React's: the one place React's types meet the story file's module.
11
26
  *
@@ -19,21 +34,63 @@ import { createElement } from "react";
19
34
  * @param story - the loaded story.
20
35
  */
21
36
  const asReactStory = (story) => story;
37
+ /** What each level of decorators draws, innermost first, for the drawing being rendered. */
38
+ const LevelsContext = createContext([]);
39
+ /** The story a decorator gets at each level, made once and kept: a component's identity is what keeps its state. */
40
+ const levels = [];
41
+ /**
42
+ * The story a decorator at a level gets: callable (`story()`) and a component (`<Story />`), the same function for every
43
+ * drawing, which draws whatever its level holds in the drawing being rendered.
44
+ *
45
+ * @param level - the level, 0 for what the innermost decorator wraps.
46
+ */
47
+ const storyAt = (level) => {
48
+ const known = levels[level];
49
+ if (known !== undefined)
50
+ return known;
51
+ const story = () => useContext(LevelsContext)[level]?.() ?? null;
52
+ levels[level] = story;
53
+ return story;
54
+ };
55
+ /**
56
+ * Where the outermost level draws, inside its levels' provider: a decorator calling `story()` reads them from there.
57
+ *
58
+ * @param props - what to draw.
59
+ */
60
+ const StoryDraw = ({ draw }) => draw();
61
+ /**
62
+ * The component a story draws in: where its render's and its decorators' hooks run.
63
+ *
64
+ * @param props - the story's render, inside its decorators.
65
+ */
66
+ const StoryBody = ({ draw, levels: drawn }) => createElement(LevelsContext.Provider, { value: drawn }, createElement(StoryDraw, { draw }));
22
67
  /**
23
68
  * The story with these args: its render (or the meta's component), inside its decorators.
24
69
  *
25
70
  * @param loaded - the story.
26
71
  * @param context - the args and the canvas its render, play and decorators read.
27
72
  * @param outer - the catalogue's decorators, outside the story's and the meta's.
73
+ * @param changeArgs - changes the story's args from its own code (Storybook's `useArgs`); none changes nothing.
28
74
  */
29
- export const storyElement = (loaded, context, outer) => {
75
+ export const storyElement = (loaded, context, outer, changeArgs = () => undefined) => {
30
76
  const story = asReactStory({ ...loaded, decorators: [...loaded.decorators, ...outer] });
31
- const renderStory = () => {
77
+ const hooks = {
78
+ id: context.id,
79
+ args: context.args,
80
+ globals: context.globals,
81
+ parameters: context.parameters,
82
+ context,
83
+ changeArgs
84
+ };
85
+ const renderStory = () => withStoryHooks(hooks, () => {
32
86
  if (story.render !== undefined)
33
87
  return story.render(context.args, context);
34
88
  return story.component === undefined ? null : createElement(story.component, context.args);
35
- };
36
- const decorated = story.decorators.reduce((inner, decorator) => () => decorator(inner, context), renderStory);
37
- return decorated();
89
+ });
90
+ // each level draws the one inside it through that level's kept component
91
+ const drawn = [renderStory];
92
+ story.decorators.forEach((decorator, level) => {
93
+ drawn.push(() => withStoryHooks(hooks, () => decorator(storyAt(level), context)));
94
+ });
95
+ return createElement(StoryBody, { key: context.id, draw: drawn.at(-1) ?? renderStory, levels: drawn });
38
96
  };
39
- //# sourceMappingURL=storyElement.js.map
@@ -1,17 +1,3 @@
1
- /**
2
- * The JSX of a story drawn from its args: the component with the values the story and the props panel set, as a
3
- * developer would write it, for the Code card.
4
- *
5
- * @remarks
6
- * A text is an attribute in quotes (in braces when it holds a quote), a number or `null` in braces, `true` the bare
7
- * name, `false` in braces, a named value by its name (`icon={IconFolder}`), a list as an array literal
8
- * (`options={["weekly", "monthly"]}`); `children` goes between the tags. Only the values that cross from the frame are
9
- * known (texts, numbers, booleans, `null`, lists of texts and numbers, named values): a function or an element the
10
- * story passes is left out. Pure.
11
- *
12
- * @module
13
- */
14
- import { type PanelArgs } from "forgebook/messages";
15
1
  import type { StoryCodeWriter } from "forgebook/renderer";
16
2
  /**
17
3
  * A component's JSX with these args.
@@ -19,7 +5,6 @@ import type { StoryCodeWriter } from "forgebook/renderer";
19
5
  * @param component - the component's name (`IconTile`).
20
6
  * @param args - the values, in the order they are written.
21
7
  */
22
- export declare const storyJsx: (component: string, args: PanelArgs) => string;
8
+ export declare const storyJsx: StoryCodeWriter;
23
9
  /** The code writer a React catalogue's shell writes a story's code with: the name the generated pages import. */
24
10
  export declare const codeOf: StoryCodeWriter;
25
- //# sourceMappingURL=storyJsx.d.ts.map
@@ -1,52 +1,16 @@
1
1
  /**
2
- * The JSX of a story drawn from its args: the component with the values the story and the props panel set, as a
3
- * developer would write it, for the Code card.
4
- *
5
- * @remarks
6
- * A text is an attribute in quotes (in braces when it holds a quote), a number or `null` in braces, `true` the bare
7
- * name, `false` in braces, a named value by its name (`icon={IconFolder}`), a list as an array literal
8
- * (`options={["weekly", "monthly"]}`); `children` goes between the tags. Only the values that cross from the frame are
9
- * known (texts, numbers, booleans, `null`, lists of texts and numbers, named values): a function or an element the
10
- * story passes is left out. Pure.
2
+ * The code writer a React catalogue's shell writes a story's code with: the JSX every framework written in JSX shares
3
+ * (`forgebook/jsx-code`), under the names the generated pages and earlier versions import.
11
4
  *
12
5
  * @module
13
6
  */
14
- import { isNamedArg } from "forgebook/messages";
15
- /** One attribute as JSX writes it. */
16
- const attribute = (name, value) => {
17
- if (value === true)
18
- return name;
19
- if (typeof value === "string")
20
- return value.includes('"') ? `${name}={${JSON.stringify(value)}}` : `${name}="${value}"`;
21
- // a named value is written by its name, as the source's module exports it (`icon={IconFolder}`)
22
- if (isNamedArg(value))
23
- return `${name}={${value.named}}`;
24
- // a multiple choice's values, as the source writes a list
25
- if (Array.isArray(value))
26
- return `${name}={[${value.map((item) => JSON.stringify(item)).join(", ")}]}`;
27
- return `${name}={${String(value)}}`;
28
- };
29
- /** The longest line the attributes stay on one line for, in characters. */
30
- const ONE_LINE = 80;
7
+ import { jsxCode } from "forgebook/jsx-code";
31
8
  /**
32
9
  * A component's JSX with these args.
33
10
  *
34
11
  * @param component - the component's name (`IconTile`).
35
12
  * @param args - the values, in the order they are written.
36
13
  */
37
- export const storyJsx = (component, args) => {
38
- const { children, ...rest } = args;
39
- const attributes = Object.entries(rest).map(([name, value]) => attribute(name, value));
40
- const inner = children === undefined || children === null || children === false ? undefined : String(children);
41
- const oneLine = [component, ...attributes].join(" ");
42
- const isOneLine = oneLine.length + 2 <= ONE_LINE;
43
- const opening = isOneLine
44
- ? `<${oneLine}`
45
- : `<${component}\n${attributes.map((text) => ` ${text}`).join("\n")}\n`;
46
- if (inner === undefined)
47
- return `${opening}${isOneLine ? " " : ""}/>`;
48
- return `${opening}>${inner}</${component}>`;
49
- };
14
+ export const storyJsx = jsxCode;
50
15
  /** The code writer a React catalogue's shell writes a story's code with: the name the generated pages import. */
51
- export const codeOf = storyJsx;
52
- //# sourceMappingURL=storyJsx.js.map
16
+ export const codeOf = jsxCode;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The providers every React story is drawn inside, in a frame and on the server alike: the story's texts and its clock.
3
+ *
4
+ * @module
5
+ */
6
+ import type { StoryDrawing } from "forgebook/renderer";
7
+ import type { ReactNode } from "react";
8
+ /**
9
+ * The story in the texts' and the clock's providers; the language's provider is a decorator of the catalogue's
10
+ * preview, inside these.
11
+ *
12
+ * @param drawing - the story's texts and clock.
13
+ * @param story - the story's element, to wrap.
14
+ */
15
+ export declare const inProviders: (drawing: StoryDrawing, story: ReactNode) => ReactNode;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StoryClockContext } from "./clock.js";
3
+ import { StoryTextsContext } from "./texts.js";
4
+ /**
5
+ * The story in the texts' and the clock's providers; the language's provider is a decorator of the catalogue's
6
+ * preview, inside these.
7
+ *
8
+ * @param drawing - the story's texts and clock.
9
+ * @param story - the story's element, to wrap.
10
+ */
11
+ export const inProviders = (drawing, story) => (_jsx(StoryTextsContext.Provider, { value: drawing.texts, children: _jsx(StoryClockContext.Provider, { value: drawing.clock, children: story }) }));
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Storybook's preview hooks that read the story (`useArgs`, `useGlobals`, `useParameter`, `useStoryContext`), for a
3
+ * React story file written for Storybook: its imports of `storybook/preview-api` reach these, and React's own for
4
+ * Storybook's `useState`, `useEffect` and the rest (`vite/storybookPreviewApi.ts`).
5
+ *
6
+ * @remarks
7
+ * Storybook runs its hooks on a state of its own, since it calls a decorator outside any framework's render. Forgebook
8
+ * draws every decorator and render of a React story inside a component (`storyElement.ts`), so Storybook's hooks are
9
+ * React's there, and a hook of each kind may be mixed in one decorator as Storybook's documentation asks not to. What
10
+ * the story is drawn with comes from the decorator or render being called: `storyElement.ts` names it while it calls
11
+ * one (`withStoryHooks`), as Storybook names its own, and on the global object so that a second copy of this module
12
+ * (a pre-bundled one beside the sources) reads the same. `useArgs`'s update and reset change the story's args as the
13
+ * panel would (`forgebook/renderer`'s `changeArgs`). Globals are the catalogue's switches, set from its toolbar: a
14
+ * story cannot change them, and Storybook's channel does not exist here.
15
+ *
16
+ * @module
17
+ */
18
+ import type { ArgsChange } from "forgebook/renderer";
19
+ /** What a story's decorators and render are drawn with, as Storybook's hooks read it. */
20
+ export type StoryHooksContext = {
21
+ /** The story's id. */
22
+ readonly id: string;
23
+ /** The args it is drawn with. */
24
+ readonly args: Readonly<Record<string, unknown>>;
25
+ /** The values of the catalogue's switches, its `context.globals`. */
26
+ readonly globals: Readonly<Record<string, string>>;
27
+ /** Its parameters. */
28
+ readonly parameters: Readonly<Record<string, unknown>>;
29
+ /** The whole context its decorator or render was called with, for `useStoryContext`. */
30
+ readonly context: Readonly<Record<string, unknown>>;
31
+ /** Changes its args as the panel would; does nothing where nothing takes them (a server render). */
32
+ readonly changeArgs: (change: ArgsChange) => void;
33
+ };
34
+ /**
35
+ * Calls a story's decorator or render with the context Storybook's hooks read while it runs.
36
+ *
37
+ * @param context - what the story is drawn with.
38
+ * @param call - the decorator's or render's call.
39
+ * @returns what it returns.
40
+ */
41
+ export declare const withStoryHooks: <TResult>(context: StoryHooksContext, call: () => TResult) => TResult;
42
+ /**
43
+ * The story's args, and how to change them, as the panel would: `updateArgs` sets values, `resetArgs` puts the named
44
+ * ones (every one, with none named) back to the story's own.
45
+ *
46
+ * @returns the args, the update and the reset.
47
+ */
48
+ export declare const useArgs: () => [Readonly<Record<string, unknown>>, (args: Readonly<Record<string, unknown>>) => void, (names?: readonly string[]) => void];
49
+ /**
50
+ * The values of the catalogue's switches, and Storybook's way to change them, which throws: they are set from the
51
+ * catalogue's toolbar.
52
+ *
53
+ * @returns the values and the change.
54
+ */
55
+ export declare const useGlobals: () => [Readonly<Record<string, string>>, (globals: unknown) => never];
56
+ /**
57
+ * One of the story's parameters.
58
+ *
59
+ * @param key - the parameter's name.
60
+ * @param defaultValue - what to give when the story has none.
61
+ */
62
+ export declare const useParameter: <TValue>(key: string, defaultValue?: TValue) => unknown;
63
+ /**
64
+ * The context the decorator or render was called with.
65
+ *
66
+ * @returns the context.
67
+ */
68
+ export declare const useStoryContext: () => Readonly<Record<string, unknown>>;
69
+ /**
70
+ * Storybook's channel to its addons, which Forgebook does not have.
71
+ *
72
+ * @throws always, saying so.
73
+ */
74
+ export declare const useChannel: () => never;