@component-anatomy/storybook 0.3.0 → 0.4.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 +47 -2
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -1
- package/dist/index.js.map +3 -3
- package/dist/preview.d.ts +12 -1
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +5 -0
- package/dist/preview.js.map +2 -2
- package/package.json +17 -12
- package/src/index.ts +43 -4
- package/src/preview.ts +21 -1
- package/dist/index.cjs +0 -48
- package/dist/index.cjs.map +0 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Storybook addon that adds an **Anatomy** panel — an interactive part list sync
|
|
|
5
5
|
- Hover a part in the panel → the element is highlighted in the canvas
|
|
6
6
|
- Hover a `data-part` element in the canvas → the panel entry activates
|
|
7
7
|
- The same table renders **inside MDX** via `@component-anatomy/storybook/blocks`
|
|
8
|
-
- Works with **Storybook
|
|
8
|
+
- Works with **Storybook 10 and 11**, any renderer (React, Vue, HTML, Web Components…)
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -13,6 +13,9 @@ Storybook addon that adds an **Anatomy** panel — an interactive part list sync
|
|
|
13
13
|
npm install --save-dev @component-anatomy/storybook
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
Register the addon in `.storybook/main.ts`. This is what loads the **Anatomy**
|
|
17
|
+
panel into the Storybook manager:
|
|
18
|
+
|
|
16
19
|
```ts
|
|
17
20
|
// .storybook/main.ts
|
|
18
21
|
export default {
|
|
@@ -20,6 +23,34 @@ export default {
|
|
|
20
23
|
};
|
|
21
24
|
```
|
|
22
25
|
|
|
26
|
+
### If your `preview.ts` uses CSF Next
|
|
27
|
+
|
|
28
|
+
CSF Next is the default story format in Storybook 11. A `preview.ts` built with
|
|
29
|
+
`definePreview` composes **only** the addons it lists, and Storybook drops the
|
|
30
|
+
preview annotations that `main.ts` would otherwise contribute — so the addon
|
|
31
|
+
also has to be registered there, or the canvas decorator never mounts and the
|
|
32
|
+
panel stays empty:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// .storybook/preview.ts
|
|
36
|
+
|
|
37
|
+
// Replace your-framework with the framework you are using (e.g. react-vite, nextjs-vite)
|
|
38
|
+
import { definePreview } from '@storybook/your-framework';
|
|
39
|
+
import componentAnatomy from '@component-anatomy/storybook';
|
|
40
|
+
|
|
41
|
+
export default definePreview({
|
|
42
|
+
// ...rest of preview
|
|
43
|
+
addons: [componentAnatomy()], // 👈 register the addon here
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Registering in both places is correct and safe: the two paths are mutually
|
|
48
|
+
exclusive, so the decorator is composed exactly once either way. A `preview.ts`
|
|
49
|
+
that is still a plain object needs only the `main.ts` entry above.
|
|
50
|
+
|
|
51
|
+
Doing so also types `parameters.anatomy` across that preview's metas and
|
|
52
|
+
stories, so the shape below is checked for you.
|
|
53
|
+
|
|
23
54
|
## Use
|
|
24
55
|
|
|
25
56
|
Annotate your story's DOM with `data-part` and add the `anatomy` parameter:
|
|
@@ -113,9 +144,23 @@ Two things worth knowing, for either block:
|
|
|
113
144
|
|
|
114
145
|
Several blocks can share one page; each talks only to the story it names.
|
|
115
146
|
|
|
147
|
+
## Compatibility
|
|
148
|
+
|
|
149
|
+
| | |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `storybook` | `^10.0.0 \|\| ^11.0.0-0` |
|
|
152
|
+
| `@storybook/addon-docs` | `^10.0.0 \|\| ^11.0.0-0` (optional — only for the MDX blocks) |
|
|
153
|
+
| `react` | `>=18` (optional — only for the MDX blocks) |
|
|
154
|
+
| Node | `>=22.12.0` |
|
|
155
|
+
|
|
156
|
+
The package is ESM-only, like Storybook itself since 9. Storybook 9 is no
|
|
157
|
+
longer supported: CSF Next registration needs `definePreviewAddon`, which
|
|
158
|
+
Storybook only ships from 9.1 onwards, and the addon is built and tested
|
|
159
|
+
against 10 and 11.
|
|
160
|
+
|
|
116
161
|
## Example
|
|
117
162
|
|
|
118
|
-
A complete Storybook
|
|
163
|
+
A complete Storybook 11 setup with Button/Slider/Tabs stories — and two MDX
|
|
119
164
|
pages using the `<Anatomy>` block — lives in [`examples/storybook`](https://github.com/julien-deramond/component-anatomy/tree/main/examples/storybook), deployed at https://julien-deramond.github.io/component-anatomy/storybook/.
|
|
120
165
|
|
|
121
166
|
## Docs
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
import type { AnatomyParameters } from './types.js';
|
|
1
2
|
export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';
|
|
2
3
|
export type { AnatomyParameters } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What this addon contributes to a CSF Next project's type context: a typed
|
|
6
|
+
* `parameters.anatomy` on every meta and story of a preview that registers it.
|
|
7
|
+
*/
|
|
8
|
+
export type ComponentAnatomyTypes = {
|
|
9
|
+
parameters: {
|
|
10
|
+
/** @see {@link AnatomyParameters} */
|
|
11
|
+
anatomy?: AnatomyParameters;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
15
|
+
/**
|
|
16
|
+
* The addon's preview annotations, for a CSF Next `preview.ts`:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { definePreview } from '@storybook/your-framework';
|
|
20
|
+
* import componentAnatomy from '@component-anatomy/storybook';
|
|
21
|
+
*
|
|
22
|
+
* export default definePreview({
|
|
23
|
+
* addons: [componentAnatomy()],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* `.storybook/main.ts` must still list the addon — `addons:
|
|
28
|
+
* ['@component-anatomy/storybook']` — since that is what loads the manager
|
|
29
|
+
* panel. What changes under CSF Next is the preview side: a `preview.ts` built
|
|
30
|
+
* with `definePreview` composes *only* its own `addons`, and Storybook drops
|
|
31
|
+
* every addon annotation main.ts would otherwise have contributed. Without the
|
|
32
|
+
* call below, the canvas decorator never mounts and the panel stays empty.
|
|
33
|
+
*
|
|
34
|
+
* Registering in both places is safe — the two paths are mutually exclusive,
|
|
35
|
+
* so the decorator is composed once either way.
|
|
36
|
+
*
|
|
37
|
+
* The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it
|
|
38
|
+
* needs React and `@storybook/addon-docs`, both optional peers that must not
|
|
39
|
+
* become load-bearing for a Storybook that only wants the panel.
|
|
40
|
+
*/
|
|
41
|
+
declare function _default(): import("storybook/internal/csf").PreviewAddon<ComponentAnatomyTypes>;
|
|
3
42
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE;QACV,qCAAqC;QACrC,OAAO,CAAC,EAAE,iBAAiB,CAAC;KAC7B,CAAC;CACH,CAAC;;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePreviewAddon } from "storybook/internal/csf";
|
|
3
|
+
|
|
4
|
+
// src/preview.ts
|
|
5
|
+
import { addons, useEffect } from "storybook/preview-api";
|
|
6
|
+
import { createAnatomy } from "@component-anatomy/core";
|
|
7
|
+
|
|
1
8
|
// src/constants.ts
|
|
2
9
|
var ADDON_ID = "component-anatomy";
|
|
3
10
|
var PANEL_ID = `${ADDON_ID}/panel`;
|
|
@@ -16,10 +23,78 @@ var EVENTS = {
|
|
|
16
23
|
/** consumers → preview: a panel/block mounted and wants the current part list. */
|
|
17
24
|
PARTS_REQUEST: `${ADDON_ID}/parts-request`
|
|
18
25
|
};
|
|
26
|
+
|
|
27
|
+
// src/channel.ts
|
|
28
|
+
var matchesStory = (eventStoryId, storyId) => !eventStoryId || !storyId || eventStoryId === storyId;
|
|
29
|
+
|
|
30
|
+
// src/preview.ts
|
|
31
|
+
var withComponentAnatomy = (storyFn, context) => {
|
|
32
|
+
const params = context.parameters?.[PARAM_KEY];
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!params || params.disable) return;
|
|
35
|
+
const channel = addons.getChannel();
|
|
36
|
+
const canvas = context.canvasElement;
|
|
37
|
+
if (!canvas) return;
|
|
38
|
+
const storyId = context.id;
|
|
39
|
+
const root = params.root ? canvas.querySelector(params.root) ?? canvas : canvas;
|
|
40
|
+
const controller = createAnatomy({
|
|
41
|
+
root,
|
|
42
|
+
parts: params.parts,
|
|
43
|
+
preset: params.preset,
|
|
44
|
+
theme: params.theme,
|
|
45
|
+
overlay: {
|
|
46
|
+
label: params.overlayLabel !== false,
|
|
47
|
+
padding: params.overlayPadding
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const announceParts = () => channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });
|
|
51
|
+
announceParts();
|
|
52
|
+
const offEnter = controller.on(
|
|
53
|
+
"part:enter",
|
|
54
|
+
(partId) => channel.emit(EVENTS.PART_ENTER, { storyId, partId })
|
|
55
|
+
);
|
|
56
|
+
const offLeave = controller.on(
|
|
57
|
+
"part:leave",
|
|
58
|
+
() => channel.emit(EVENTS.PART_LEAVE, { storyId })
|
|
59
|
+
);
|
|
60
|
+
const onHoverItem = (event) => {
|
|
61
|
+
if (!matchesStory(event?.storyId, storyId)) return;
|
|
62
|
+
controller.highlight(event.partId);
|
|
63
|
+
};
|
|
64
|
+
const onLeaveItem = (event = {}) => {
|
|
65
|
+
if (!matchesStory(event?.storyId, storyId)) return;
|
|
66
|
+
controller.unhighlight();
|
|
67
|
+
};
|
|
68
|
+
const onPartsRequest = (event = {}) => {
|
|
69
|
+
if (!matchesStory(event?.storyId, storyId)) return;
|
|
70
|
+
announceParts();
|
|
71
|
+
};
|
|
72
|
+
channel.on(EVENTS.HOVER_ITEM, onHoverItem);
|
|
73
|
+
channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);
|
|
74
|
+
channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);
|
|
75
|
+
return () => {
|
|
76
|
+
channel.off(EVENTS.HOVER_ITEM, onHoverItem);
|
|
77
|
+
channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);
|
|
78
|
+
channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);
|
|
79
|
+
offEnter();
|
|
80
|
+
offLeave();
|
|
81
|
+
controller.destroy();
|
|
82
|
+
};
|
|
83
|
+
}, [context.id]);
|
|
84
|
+
return storyFn();
|
|
85
|
+
};
|
|
86
|
+
var annotations = {
|
|
87
|
+
decorators: [withComponentAnatomy]
|
|
88
|
+
};
|
|
89
|
+
var preview_default = annotations;
|
|
90
|
+
|
|
91
|
+
// src/index.ts
|
|
92
|
+
var index_default = () => definePreviewAddon(preview_default);
|
|
19
93
|
export {
|
|
20
94
|
ADDON_ID,
|
|
21
95
|
EVENTS,
|
|
22
96
|
PANEL_ID,
|
|
23
|
-
PARAM_KEY
|
|
97
|
+
PARAM_KEY,
|
|
98
|
+
index_default as default
|
|
24
99
|
};
|
|
25
100
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/constants.ts"],
|
|
4
|
-
"sourcesContent": ["export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n"],
|
|
5
|
-
"mappings": ";
|
|
3
|
+
"sources": ["../src/index.ts", "../src/preview.ts", "../src/constants.ts", "../src/channel.ts"],
|
|
4
|
+
"sourcesContent": ["import { definePreviewAddon } from 'storybook/internal/csf';\n\nimport annotations from './preview.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';\nexport type { AnatomyParameters } from './types.js';\n\n/**\n * What this addon contributes to a CSF Next project's type context: a typed\n * `parameters.anatomy` on every meta and story of a preview that registers it.\n */\nexport type ComponentAnatomyTypes = {\n parameters: {\n /** @see {@link AnatomyParameters} */\n anatomy?: AnatomyParameters;\n };\n};\n\n/**\n * The addon's preview annotations, for a CSF Next `preview.ts`:\n *\n * ```ts\n * import { definePreview } from '@storybook/your-framework';\n * import componentAnatomy from '@component-anatomy/storybook';\n *\n * export default definePreview({\n * addons: [componentAnatomy()],\n * });\n * ```\n *\n * `.storybook/main.ts` must still list the addon \u2014 `addons:\n * ['@component-anatomy/storybook']` \u2014 since that is what loads the manager\n * panel. What changes under CSF Next is the preview side: a `preview.ts` built\n * with `definePreview` composes *only* its own `addons`, and Storybook drops\n * every addon annotation main.ts would otherwise have contributed. Without the\n * call below, the canvas decorator never mounts and the panel stays empty.\n *\n * Registering in both places is safe \u2014 the two paths are mutually exclusive,\n * so the decorator is composed once either way.\n *\n * The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it\n * needs React and `@storybook/addon-docs`, both optional peers that must not\n * become load-bearing for a Storybook that only wants the panel.\n */\nexport default () => definePreviewAddon<ComponentAnatomyTypes>(annotations);\n", "/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type {\n ProjectAnnotations,\n Renderer,\n PartialStoryFn,\n StoryContext,\n} from 'storybook/internal/types';\nimport { createAnatomy } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { HoverItemEvent, StoryScopedEvent } from './channel.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport const withComponentAnatomy = (\n storyFn: PartialStoryFn<Renderer>,\n context: StoryContext<Renderer>\n) => {\n const params = context.parameters?.[PARAM_KEY] as AnatomyParameters | undefined;\n\n useEffect(() => {\n if (!params || params.disable) return;\n\n const channel = addons.getChannel();\n const canvas = context.canvasElement as unknown as HTMLElement;\n if (!canvas) return;\n\n const storyId = context.id;\n\n const root = params.root\n ? canvas.querySelector<HTMLElement>(params.root) ?? canvas\n : canvas;\n\n const controller = createAnatomy({\n root,\n parts: params.parts,\n preset: params.preset,\n theme: params.theme,\n overlay: {\n label: params.overlayLabel !== false,\n padding: params.overlayPadding,\n },\n });\n\n const announceParts = () =>\n channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });\n\n announceParts();\n\n const offEnter = controller.on('part:enter', (partId) =>\n channel.emit(EVENTS.PART_ENTER, { storyId, partId })\n );\n const offLeave = controller.on('part:leave', () =>\n channel.emit(EVENTS.PART_LEAVE, { storyId })\n );\n\n // A docs page mounts several stories at once, so every controller sees\n // every panel/block event \u2014 only act on the ones addressed to this story.\n const onHoverItem = (event: HoverItemEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.highlight(event.partId);\n };\n const onLeaveItem = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.unhighlight();\n };\n const onPartsRequest = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n announceParts();\n };\n\n channel.on(EVENTS.HOVER_ITEM, onHoverItem);\n channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);\n // The panel or block may mount after the story rendered \u2014 let it ask for\n // the list rather than racing the first announcement.\n channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);\n\n return () => {\n channel.off(EVENTS.HOVER_ITEM, onHoverItem);\n channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);\n channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);\n offEnter();\n offLeave();\n controller.destroy();\n };\n }, [context.id]);\n\n return storyFn();\n};\n\nexport const decorators = [withComponentAnatomy];\n\n/**\n * The same annotations as a default export, which is the shape\n * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and\n * the shape a consumer gets from `@component-anatomy/storybook/preview`.\n *\n * Storybook reads `module.default[field] ?? module[field]`, so a preview\n * annotation module that exports both is read exactly once \u2014 the named\n * `decorators` above stays for anyone importing it directly.\n */\nconst annotations: ProjectAnnotations<Renderer> = {\n decorators: [withComponentAnatomy],\n};\n\nexport default annotations;\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,0BAA0B;;;ACWnC,SAAS,QAAQ,iBAAiB;AAOlC,SAAS,qBAAqB;;;AClBvB,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;;;ACKO,IAAM,eAAe,CAC1B,cACA,YACY,CAAC,gBAAgB,CAAC,WAAW,iBAAiB;;;AFTrD,IAAM,uBAAuB,CAClC,SACA,YACG;AACH,QAAM,SAAS,QAAQ,aAAa,SAAS;AAE7C,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,QAAS;AAE/B,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,QAAQ;AAExB,UAAM,OAAO,OAAO,OAChB,OAAO,cAA2B,OAAO,IAAI,KAAK,SAClD;AAEJ,UAAM,aAAa,cAAc;AAAA,MAC/B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,QACP,OAAO,OAAO,iBAAiB;AAAA,QAC/B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,MACpB,QAAQ,KAAK,OAAO,OAAO,EAAE,SAAS,OAAO,WAAW,SAAS,EAAE,CAAC;AAEtE,kBAAc;AAEd,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,CAAC,WAC5C,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD;AACA,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,MAC3C,QAAQ,KAAK,OAAO,YAAY,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAIA,UAAM,cAAc,CAAC,UAA0B;AAC7C,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,UAAU,MAAM,MAAM;AAAA,IACnC;AACA,UAAM,cAAc,CAAC,QAA0B,CAAC,MAAM;AACpD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,YAAY;AAAA,IACzB;AACA,UAAM,iBAAiB,CAAC,QAA0B,CAAC,MAAM;AACvD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,oBAAc;AAAA,IAChB;AAEA,YAAQ,GAAG,OAAO,YAAY,WAAW;AACzC,YAAQ,GAAG,OAAO,YAAY,WAAW;AAGzC,YAAQ,GAAG,OAAO,eAAe,cAAc;AAE/C,WAAO,MAAM;AACX,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,eAAe,cAAc;AAChD,eAAS;AACT,eAAS;AACT,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEf,SAAO,QAAQ;AACjB;AAaA,IAAM,cAA4C;AAAA,EAChD,YAAY,CAAC,oBAAoB;AACnC;AAEA,IAAO,kBAAQ;;;ADvEf,IAAO,gBAAQ,MAAM,mBAA0C,eAAW;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/preview.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import type { Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';
|
|
1
|
+
import type { ProjectAnnotations, Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';
|
|
2
2
|
export declare const withComponentAnatomy: (storyFn: PartialStoryFn<Renderer>, context: StoryContext<Renderer>) => any;
|
|
3
3
|
export declare const decorators: (typeof withComponentAnatomy)[];
|
|
4
|
+
/**
|
|
5
|
+
* The same annotations as a default export, which is the shape
|
|
6
|
+
* `definePreviewAddon` takes in the package's main entry (see `index.ts`) and
|
|
7
|
+
* the shape a consumer gets from `@component-anatomy/storybook/preview`.
|
|
8
|
+
*
|
|
9
|
+
* Storybook reads `module.default[field] ?? module[field]`, so a preview
|
|
10
|
+
* annotation module that exports both is read exactly once — the named
|
|
11
|
+
* `decorators` above stays for anyone importing it directly.
|
|
12
|
+
*/
|
|
13
|
+
declare const annotations: ProjectAnnotations<Renderer>;
|
|
14
|
+
export default annotations;
|
|
4
15
|
//# sourceMappingURL=preview.d.ts.map
|
package/dist/preview.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,YAAY,EACb,MAAM,0BAA0B,CAAC;AAQlC,eAAO,MAAM,oBAAoB,YACtB,cAAc,CAAC,QAAQ,CAAC,WACxB,YAAY,CAAC,QAAQ,CAAC,QAwEhC,CAAC;AAEF,eAAO,MAAM,UAAU,iCAAyB,CAAC;AAEjD;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAE7C,CAAC;eAEa,WAAW"}
|
package/dist/preview.js
CHANGED
|
@@ -81,8 +81,13 @@ var withComponentAnatomy = (storyFn, context) => {
|
|
|
81
81
|
return storyFn();
|
|
82
82
|
};
|
|
83
83
|
var decorators = [withComponentAnatomy];
|
|
84
|
+
var annotations = {
|
|
85
|
+
decorators: [withComponentAnatomy]
|
|
86
|
+
};
|
|
87
|
+
var preview_default = annotations;
|
|
84
88
|
export {
|
|
85
89
|
decorators,
|
|
90
|
+
preview_default as default,
|
|
86
91
|
withComponentAnatomy
|
|
87
92
|
};
|
|
88
93
|
//# sourceMappingURL=preview.js.map
|
package/dist/preview.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/preview.ts", "../src/constants.ts", "../src/channel.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type {
|
|
5
|
-
"mappings": ";AAWA,SAAS,QAAQ,iBAAiB;
|
|
4
|
+
"sourcesContent": ["/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type {\n ProjectAnnotations,\n Renderer,\n PartialStoryFn,\n StoryContext,\n} from 'storybook/internal/types';\nimport { createAnatomy } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { HoverItemEvent, StoryScopedEvent } from './channel.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport const withComponentAnatomy = (\n storyFn: PartialStoryFn<Renderer>,\n context: StoryContext<Renderer>\n) => {\n const params = context.parameters?.[PARAM_KEY] as AnatomyParameters | undefined;\n\n useEffect(() => {\n if (!params || params.disable) return;\n\n const channel = addons.getChannel();\n const canvas = context.canvasElement as unknown as HTMLElement;\n if (!canvas) return;\n\n const storyId = context.id;\n\n const root = params.root\n ? canvas.querySelector<HTMLElement>(params.root) ?? canvas\n : canvas;\n\n const controller = createAnatomy({\n root,\n parts: params.parts,\n preset: params.preset,\n theme: params.theme,\n overlay: {\n label: params.overlayLabel !== false,\n padding: params.overlayPadding,\n },\n });\n\n const announceParts = () =>\n channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });\n\n announceParts();\n\n const offEnter = controller.on('part:enter', (partId) =>\n channel.emit(EVENTS.PART_ENTER, { storyId, partId })\n );\n const offLeave = controller.on('part:leave', () =>\n channel.emit(EVENTS.PART_LEAVE, { storyId })\n );\n\n // A docs page mounts several stories at once, so every controller sees\n // every panel/block event \u2014 only act on the ones addressed to this story.\n const onHoverItem = (event: HoverItemEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.highlight(event.partId);\n };\n const onLeaveItem = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.unhighlight();\n };\n const onPartsRequest = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n announceParts();\n };\n\n channel.on(EVENTS.HOVER_ITEM, onHoverItem);\n channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);\n // The panel or block may mount after the story rendered \u2014 let it ask for\n // the list rather than racing the first announcement.\n channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);\n\n return () => {\n channel.off(EVENTS.HOVER_ITEM, onHoverItem);\n channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);\n channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);\n offEnter();\n offLeave();\n controller.destroy();\n };\n }, [context.id]);\n\n return storyFn();\n};\n\nexport const decorators = [withComponentAnatomy];\n\n/**\n * The same annotations as a default export, which is the shape\n * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and\n * the shape a consumer gets from `@component-anatomy/storybook/preview`.\n *\n * Storybook reads `module.default[field] ?? module[field]`, so a preview\n * annotation module that exports both is read exactly once \u2014 the named\n * `decorators` above stays for anyone importing it directly.\n */\nconst annotations: ProjectAnnotations<Renderer> = {\n decorators: [withComponentAnatomy],\n};\n\nexport default annotations;\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n"],
|
|
5
|
+
"mappings": ";AAWA,SAAS,QAAQ,iBAAiB;AAOlC,SAAS,qBAAqB;;;AClBvB,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;;;ACKO,IAAM,eAAe,CAC1B,cACA,YACY,CAAC,gBAAgB,CAAC,WAAW,iBAAiB;;;AFTrD,IAAM,uBAAuB,CAClC,SACA,YACG;AACH,QAAM,SAAS,QAAQ,aAAa,SAAS;AAE7C,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,QAAS;AAE/B,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,QAAQ;AAExB,UAAM,OAAO,OAAO,OAChB,OAAO,cAA2B,OAAO,IAAI,KAAK,SAClD;AAEJ,UAAM,aAAa,cAAc;AAAA,MAC/B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,QACP,OAAO,OAAO,iBAAiB;AAAA,QAC/B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,MACpB,QAAQ,KAAK,OAAO,OAAO,EAAE,SAAS,OAAO,WAAW,SAAS,EAAE,CAAC;AAEtE,kBAAc;AAEd,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,CAAC,WAC5C,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD;AACA,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,MAC3C,QAAQ,KAAK,OAAO,YAAY,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAIA,UAAM,cAAc,CAAC,UAA0B;AAC7C,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,UAAU,MAAM,MAAM;AAAA,IACnC;AACA,UAAM,cAAc,CAAC,QAA0B,CAAC,MAAM;AACpD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,YAAY;AAAA,IACzB;AACA,UAAM,iBAAiB,CAAC,QAA0B,CAAC,MAAM;AACvD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,oBAAc;AAAA,IAChB;AAEA,YAAQ,GAAG,OAAO,YAAY,WAAW;AACzC,YAAQ,GAAG,OAAO,YAAY,WAAW;AAGzC,YAAQ,GAAG,OAAO,eAAe,cAAc;AAE/C,WAAO,MAAM;AACX,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,eAAe,cAAc;AAChD,eAAS;AACT,eAAS;AACT,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEf,SAAO,QAAQ;AACjB;AAEO,IAAM,aAAa,CAAC,oBAAoB;AAW/C,IAAM,cAA4C;AAAA,EAChD,YAAY,CAAC,oBAAoB;AACnC;AAEA,IAAO,kBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@component-anatomy/storybook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Storybook addon — interactive component anatomy panel synced with the story canvas",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Julien Déramond",
|
|
@@ -37,18 +37,23 @@
|
|
|
37
37
|
"exports": {
|
|
38
38
|
".": {
|
|
39
39
|
"types": "./dist/index.d.ts",
|
|
40
|
-
"
|
|
41
|
-
"require": "./dist/index.cjs"
|
|
40
|
+
"default": "./dist/index.js"
|
|
42
41
|
},
|
|
43
42
|
"./blocks": {
|
|
44
43
|
"types": "./dist/blocks.d.ts",
|
|
45
|
-
"
|
|
44
|
+
"default": "./dist/blocks.js"
|
|
45
|
+
},
|
|
46
|
+
"./manager": {
|
|
47
|
+
"types": "./dist/manager.d.ts",
|
|
48
|
+
"default": "./dist/manager.js"
|
|
49
|
+
},
|
|
50
|
+
"./preview": {
|
|
51
|
+
"types": "./dist/preview.d.ts",
|
|
52
|
+
"default": "./dist/preview.js"
|
|
46
53
|
},
|
|
47
|
-
"./manager": "./dist/manager.js",
|
|
48
|
-
"./preview": "./dist/preview.js",
|
|
49
54
|
"./package.json": "./package.json"
|
|
50
55
|
},
|
|
51
|
-
"main": "./dist/index.
|
|
56
|
+
"main": "./dist/index.js",
|
|
52
57
|
"module": "./dist/index.js",
|
|
53
58
|
"types": "./dist/index.d.ts",
|
|
54
59
|
"files": [
|
|
@@ -63,18 +68,18 @@
|
|
|
63
68
|
"@component-anatomy/core": "^0.1.0"
|
|
64
69
|
},
|
|
65
70
|
"devDependencies": {
|
|
66
|
-
"@storybook/addon-docs": "
|
|
71
|
+
"@storybook/addon-docs": "11.0.0-alpha.0",
|
|
67
72
|
"@types/react": "^19.2.18",
|
|
68
73
|
"esbuild": "^0.28.2",
|
|
69
74
|
"react": "^19.2.8",
|
|
70
75
|
"react-dom": "^19.2.8",
|
|
71
|
-
"storybook": "
|
|
76
|
+
"storybook": "11.0.0-alpha.0",
|
|
72
77
|
"typescript": "^7.0.2"
|
|
73
78
|
},
|
|
74
79
|
"peerDependencies": {
|
|
75
|
-
"@storybook/addon-docs": "
|
|
80
|
+
"@storybook/addon-docs": "^10.0.0 || ^11.0.0-0",
|
|
76
81
|
"react": ">=18",
|
|
77
|
-
"storybook": "
|
|
82
|
+
"storybook": "^10.0.0 || ^11.0.0-0"
|
|
78
83
|
},
|
|
79
84
|
"peerDependenciesMeta": {
|
|
80
85
|
"@storybook/addon-docs": {
|
|
@@ -90,6 +95,6 @@
|
|
|
90
95
|
"scripts": {
|
|
91
96
|
"build": "node build.mjs",
|
|
92
97
|
"typecheck": "tsc --noEmit",
|
|
93
|
-
"test": "node build.mjs && node ../../scripts/typecheck-fixture.mjs test/consumer-types.ts && node ../../scripts/typecheck-fixture.mjs test/consumer-blocks-types.tsx"
|
|
98
|
+
"test": "node build.mjs && node ../../scripts/typecheck-fixture.mjs test/consumer-types.ts && node ../../scripts/typecheck-fixture.mjs test/consumer-blocks-types.tsx && node ../../scripts/typecheck-fixture.mjs test/consumer-csf-next-types.ts"
|
|
94
99
|
}
|
|
95
100
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,46 @@
|
|
|
1
|
+
import { definePreviewAddon } from 'storybook/internal/csf';
|
|
2
|
+
|
|
3
|
+
import annotations from './preview.js';
|
|
4
|
+
import type { AnatomyParameters } from './types.js';
|
|
5
|
+
|
|
1
6
|
export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';
|
|
2
7
|
export type { AnatomyParameters } from './types.js';
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* What this addon contributes to a CSF Next project's type context: a typed
|
|
11
|
+
* `parameters.anatomy` on every meta and story of a preview that registers it.
|
|
12
|
+
*/
|
|
13
|
+
export type ComponentAnatomyTypes = {
|
|
14
|
+
parameters: {
|
|
15
|
+
/** @see {@link AnatomyParameters} */
|
|
16
|
+
anatomy?: AnatomyParameters;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The addon's preview annotations, for a CSF Next `preview.ts`:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { definePreview } from '@storybook/your-framework';
|
|
25
|
+
* import componentAnatomy from '@component-anatomy/storybook';
|
|
26
|
+
*
|
|
27
|
+
* export default definePreview({
|
|
28
|
+
* addons: [componentAnatomy()],
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* `.storybook/main.ts` must still list the addon — `addons:
|
|
33
|
+
* ['@component-anatomy/storybook']` — since that is what loads the manager
|
|
34
|
+
* panel. What changes under CSF Next is the preview side: a `preview.ts` built
|
|
35
|
+
* with `definePreview` composes *only* its own `addons`, and Storybook drops
|
|
36
|
+
* every addon annotation main.ts would otherwise have contributed. Without the
|
|
37
|
+
* call below, the canvas decorator never mounts and the panel stays empty.
|
|
38
|
+
*
|
|
39
|
+
* Registering in both places is safe — the two paths are mutually exclusive,
|
|
40
|
+
* so the decorator is composed once either way.
|
|
41
|
+
*
|
|
42
|
+
* The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it
|
|
43
|
+
* needs React and `@storybook/addon-docs`, both optional peers that must not
|
|
44
|
+
* become load-bearing for a Storybook that only wants the panel.
|
|
45
|
+
*/
|
|
46
|
+
export default () => definePreviewAddon<ComponentAnatomyTypes>(annotations);
|
package/src/preview.ts
CHANGED
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
* hover sync work inside MDX.
|
|
11
11
|
*/
|
|
12
12
|
import { addons, useEffect } from 'storybook/preview-api';
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
ProjectAnnotations,
|
|
15
|
+
Renderer,
|
|
16
|
+
PartialStoryFn,
|
|
17
|
+
StoryContext,
|
|
18
|
+
} from 'storybook/internal/types';
|
|
14
19
|
import { createAnatomy } from '@component-anatomy/core';
|
|
15
20
|
|
|
16
21
|
import { EVENTS, PARAM_KEY } from './constants.js';
|
|
@@ -95,3 +100,18 @@ export const withComponentAnatomy = (
|
|
|
95
100
|
};
|
|
96
101
|
|
|
97
102
|
export const decorators = [withComponentAnatomy];
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The same annotations as a default export, which is the shape
|
|
106
|
+
* `definePreviewAddon` takes in the package's main entry (see `index.ts`) and
|
|
107
|
+
* the shape a consumer gets from `@component-anatomy/storybook/preview`.
|
|
108
|
+
*
|
|
109
|
+
* Storybook reads `module.default[field] ?? module[field]`, so a preview
|
|
110
|
+
* annotation module that exports both is read exactly once — the named
|
|
111
|
+
* `decorators` above stays for anyone importing it directly.
|
|
112
|
+
*/
|
|
113
|
+
const annotations: ProjectAnnotations<Renderer> = {
|
|
114
|
+
decorators: [withComponentAnatomy],
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default annotations;
|
package/dist/index.cjs
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
ADDON_ID: () => ADDON_ID,
|
|
24
|
-
EVENTS: () => EVENTS,
|
|
25
|
-
PANEL_ID: () => PANEL_ID,
|
|
26
|
-
PARAM_KEY: () => PARAM_KEY
|
|
27
|
-
});
|
|
28
|
-
module.exports = __toCommonJS(index_exports);
|
|
29
|
-
|
|
30
|
-
// src/constants.ts
|
|
31
|
-
var ADDON_ID = "component-anatomy";
|
|
32
|
-
var PANEL_ID = `${ADDON_ID}/panel`;
|
|
33
|
-
var PARAM_KEY = "anatomy";
|
|
34
|
-
var EVENTS = {
|
|
35
|
-
/** preview → consumers: a part became active in the canvas (hover/programmatic). */
|
|
36
|
-
PART_ENTER: `${ADDON_ID}/part-enter`,
|
|
37
|
-
/** preview → consumers: no part is active anymore. */
|
|
38
|
-
PART_LEAVE: `${ADDON_ID}/part-leave`,
|
|
39
|
-
/** preview → consumers: resolved part list for a story. */
|
|
40
|
-
PARTS: `${ADDON_ID}/parts`,
|
|
41
|
-
/** consumers → preview: the user hovers/focuses a panel entry. */
|
|
42
|
-
HOVER_ITEM: `${ADDON_ID}/hover-item`,
|
|
43
|
-
/** consumers → preview: the user left a panel entry. */
|
|
44
|
-
LEAVE_ITEM: `${ADDON_ID}/leave-item`,
|
|
45
|
-
/** consumers → preview: a panel/block mounted and wants the current part list. */
|
|
46
|
-
PARTS_REQUEST: `${ADDON_ID}/parts-request`
|
|
47
|
-
};
|
|
48
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/constants.ts"],
|
|
4
|
-
"sourcesContent": ["export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';\nexport type { AnatomyParameters } from './types.js';\n\n// The `<Anatomy>` doc block lives in the `./blocks` subpath, not here: this\n// entry is loaded at config time by `.storybook/main.ts` (and built to CJS),\n// while the block needs React and `@storybook/addon-docs`, both optional\n// peers that must not become load-bearing for `addons: ['...']` to work.\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|