@gtkx/storybook 2.0.0-beta.12 → 2.0.0-beta.13

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 (42) hide show
  1. package/dist/catalog.d.ts +1 -1
  2. package/dist/catalog.d.ts.map +1 -1
  3. package/dist/catalog.js +4 -1
  4. package/dist/catalog.js.map +1 -1
  5. package/dist/config.d.ts +4 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/controls.d.ts +2 -1
  9. package/dist/controls.d.ts.map +1 -1
  10. package/dist/controls.js +9 -5
  11. package/dist/controls.js.map +1 -1
  12. package/dist/explorer-navigator.d.ts.map +1 -1
  13. package/dist/explorer-navigator.js +29 -2
  14. package/dist/explorer-navigator.js.map +1 -1
  15. package/dist/explorer-preview.d.ts.map +1 -1
  16. package/dist/explorer-preview.js +23 -4
  17. package/dist/explorer-preview.js.map +1 -1
  18. package/dist/explorer-view.d.ts +5 -1
  19. package/dist/explorer-view.d.ts.map +1 -1
  20. package/dist/explorer-view.js +17 -7
  21. package/dist/explorer-view.js.map +1 -1
  22. package/dist/explorer.d.ts +4 -0
  23. package/dist/explorer.d.ts.map +1 -1
  24. package/dist/explorer.js +6 -1
  25. package/dist/explorer.js.map +1 -1
  26. package/dist/types.d.ts +8 -2
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/types.js.map +1 -1
  29. package/dist/upstream.d.ts +4 -2
  30. package/dist/upstream.d.ts.map +1 -1
  31. package/dist/upstream.js +3 -1
  32. package/dist/upstream.js.map +1 -1
  33. package/package.json +9 -9
  34. package/src/catalog.ts +13 -10
  35. package/src/config.ts +4 -1
  36. package/src/controls.tsx +12 -6
  37. package/src/explorer-navigator.tsx +45 -4
  38. package/src/explorer-preview.tsx +55 -19
  39. package/src/explorer-view.tsx +40 -11
  40. package/src/explorer.ts +12 -1
  41. package/src/types.ts +19 -7
  42. package/src/upstream.ts +6 -2
@@ -10,7 +10,7 @@ import {
10
10
  AdwWindowTitle,
11
11
  } from "@gtkx/jsx/adw";
12
12
  import { quit } from "@gtkx/react";
13
- import { useCallback, useState, useSyncExternalStore } from "react";
13
+ import { useCallback, useEffect, useEffectEvent, useState, useSyncExternalStore } from "react";
14
14
  import type { StoryCatalog } from "./catalog.js";
15
15
  import { StoryNavigator } from "./explorer-navigator.js";
16
16
  import { StorybookPreview } from "./explorer-preview.js";
@@ -23,31 +23,53 @@ type StorybookProps = {
23
23
  applicationId?: string;
24
24
  /** Explorer window and sidebar title; defaults to GTKX Storybook. */
25
25
  title?: string;
26
+ /** Story selected once the catalog lists it; the first story is selected otherwise. */
27
+ initialStoryId?: string | undefined;
28
+ /** Called with the identifier of each story the explorer selects. */
29
+ onStorySelected?: ((id: string) => void) | undefined;
26
30
  };
27
31
 
28
- type StorybookViewProps = Pick<StorybookProps, "catalog" | "title">;
32
+ type StorybookViewProps = Pick<StorybookProps, "catalog" | "title" | "initialStoryId" | "onStorySelected">;
29
33
 
30
- const StorybookView = ({ catalog, title = "GTKX Storybook" }: StorybookViewProps): ReactNode => {
34
+ const storyCount = (count: number): string => (count === 1 ? "1 story" : `${String(count)} stories`);
35
+
36
+ const StorybookView = ({
37
+ catalog,
38
+ title = "GTKX Storybook",
39
+ initialStoryId,
40
+ onStorySelected,
41
+ }: StorybookViewProps): ReactNode => {
31
42
  const subscribe = useCallback((listener: () => void) => catalog.subscribe(listener), [catalog]);
32
43
  const getSnapshot = useCallback(() => catalog.getSnapshot(), [catalog]);
33
44
  const snapshot = useSyncExternalStore(subscribe, getSnapshot);
34
- const [selectedId, setSelectedId] = useState<string>();
45
+ const [selectedId, setSelectedId] = useState(initialStoryId);
35
46
  const selected = snapshot.stories.find((entry) => entry.id === selectedId) ?? snapshot.stories[0];
36
47
 
37
- if (selectedId !== selected?.id) {
38
- setSelectedId(selected?.id);
48
+ if (selected !== undefined && selectedId !== selected.id) {
49
+ setSelectedId(selected.id);
39
50
  }
40
51
 
52
+ const selectedStoryId = selected?.id;
53
+ const reportSelection = useEffectEvent((id: string) => {
54
+ onStorySelected?.(id);
55
+ });
56
+
57
+ useEffect(() => {
58
+ if (selectedStoryId !== undefined) {
59
+ reportSelection(selectedStoryId);
60
+ }
61
+ }, [selectedStoryId]);
62
+
41
63
  return (
42
64
  <AdwOverlaySplitView
43
65
  minSidebarWidth={220}
44
66
  maxSidebarWidth={300}
45
67
  sidebar={
46
68
  <AdwToolbarView
47
- topBar={
69
+ top={
48
70
  <AdwHeaderBar
49
71
  titleWidget={
50
- <AdwWindowTitle title={title} subtitle={`${String(snapshot.stories.length)} stories`} />
72
+ <AdwWindowTitle title={title} subtitle={storyCount(snapshot.stories.length)} />
51
73
  }
52
74
  />
53
75
  }
@@ -55,7 +77,7 @@ const StorybookView = ({ catalog, title = "GTKX Storybook" }: StorybookViewProps
55
77
  <StoryNavigator
56
78
  stories={snapshot.stories}
57
79
  errors={snapshot.errors}
58
- selectedId={selected?.id}
80
+ selectedId={selectedStoryId}
59
81
  onSelect={setSelectedId}
60
82
  />
61
83
  </AdwToolbarView>
@@ -64,7 +86,7 @@ const StorybookView = ({ catalog, title = "GTKX Storybook" }: StorybookViewProps
64
86
  {selected ? (
65
87
  <StorybookPreview key={selected.id} entry={selected} />
66
88
  ) : (
67
- <AdwToolbarView topBar={<AdwHeaderBar />}>
89
+ <AdwToolbarView top={<AdwHeaderBar />}>
68
90
  <AdwStatusPage
69
91
  name="storybook-empty"
70
92
  title={snapshot.isLoading ? "Loading stories" : "Choose a story"}
@@ -82,10 +104,17 @@ const Storybook = ({
82
104
  catalog,
83
105
  applicationId = "org.gtkx.Storybook",
84
106
  title = "GTKX Storybook",
107
+ initialStoryId,
108
+ onStorySelected,
85
109
  }: StorybookProps): ReactNode => (
86
110
  <AdwApplication applicationId={applicationId} flags={Gio.ApplicationFlags.NON_UNIQUE}>
87
111
  <AdwApplicationWindow title={title} defaultWidth={1200} defaultHeight={800} onCloseRequest={quit}>
88
- <StorybookView catalog={catalog} title={title} />
112
+ <StorybookView
113
+ catalog={catalog}
114
+ title={title}
115
+ initialStoryId={initialStoryId}
116
+ onStorySelected={onStorySelected}
117
+ />
89
118
  </AdwApplicationWindow>
90
119
  </AdwApplication>
91
120
  );
package/src/explorer.ts CHANGED
@@ -19,10 +19,21 @@ type StorybookSession = {
19
19
  const startStorybook = (options: {
20
20
  /** Gio application identifier assigned to the explorer application. */
21
21
  applicationId: string;
22
+ /** Story to select once it is loaded, such as the one a restarted explorer was showing. */
23
+ initialStoryId?: string | undefined;
24
+ /** Called with the identifier of each story the explorer selects. */
25
+ onStorySelected?: ((id: string) => void) | undefined;
22
26
  }): StorybookSession => {
23
27
  const catalog = new StoryCatalog();
24
28
  const root = createRoot(undefined, { onUncaughtError: catalog.reportError.bind(catalog) });
25
- root.render(createElement(Storybook, { catalog, applicationId: options.applicationId }));
29
+ root.render(
30
+ createElement(Storybook, {
31
+ catalog,
32
+ applicationId: options.applicationId,
33
+ initialStoryId: options.initialStoryId,
34
+ onStorySelected: options.onStorySelected,
35
+ }),
36
+ );
26
37
 
27
38
  return {
28
39
  updateStories: (sources, preview) => catalog.load(sources, preview),
package/src/types.ts CHANGED
@@ -154,21 +154,33 @@ type StoryModule = {
154
154
  default: unknown;
155
155
  };
156
156
 
157
+ /** Whether a story's declared args name only args of the component metadata, as stories typed from the metadata do. */
158
+ type IsMetaArgs<TArgs, TMetaArgs> = [Exclude<keyof TArgs, keyof TMetaArgs>] extends [never] ? true : false;
159
+
160
+ /** Args of a story export: the component metadata's args, or the args type the story declares itself. */
161
+ type StoryArgs<TModule extends StoryModule, TStory> =
162
+ TStory extends StoryAnnotations<infer TArgs extends object>
163
+ ? IsMetaArgs<TArgs, ArgsFrom<TModule["default"]>> extends true
164
+ ? TStory extends StoryAnnotations<ArgsFrom<TModule["default"]>>
165
+ ? ArgsFrom<TModule["default"]>
166
+ : TArgs
167
+ : TArgs
168
+ : never;
169
+
157
170
  /** Named module exports whose types match CSF3 stories, excluding module bookkeeping. */
158
171
  type StoryKeys<TModule extends StoryModule> = {
159
172
  [Key in keyof TModule]: Key extends "default" | "__esModule" | "__namedExportsOrder"
160
173
  ? never
161
- : TModule[Key] extends StoryAnnotations<ArgsFrom<TModule["default"]>>
162
- ? Key
163
- : never;
174
+ : [StoryArgs<TModule, TModule[Key]>] extends [never]
175
+ ? never
176
+ : Key;
164
177
  }[keyof TModule] &
165
178
  string;
166
179
 
167
180
  /** Composed stories keyed by their original named exports. */
168
- type ComposedStories<TModule extends StoryModule> = Record<
169
- StoryKeys<TModule>,
170
- ComposedStory<ArgsFrom<TModule["default"]>>
171
- >;
181
+ type ComposedStories<TModule extends StoryModule> = {
182
+ [Key in StoryKeys<TModule>]: ComposedStory<StoryArgs<TModule, TModule[Key]>>;
183
+ };
172
184
 
173
185
  export type {
174
186
  Args,
package/src/upstream.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import type { InputType } from "storybook/internal/types";
2
+ import { includeConditionalArg } from "storybook/internal/csf";
1
3
  import { composeStory } from "storybook/preview-api";
2
- import type { ComponentMeta, ComposedStory, Preview, StoryAnnotations, StoryRender } from "./types.js";
4
+ import type { Args, ComponentMeta, ComposedStory, Preview, StoryAnnotations, StoryRender } from "./types.js";
3
5
 
4
6
  type ComposeStory = <TArgs extends object>(
5
7
  ...annotations: [
@@ -13,6 +15,8 @@ type ComposeStory = <TArgs extends object>(
13
15
 
14
16
  const composeStoryAnnotations = composeStory as ComposeStory;
15
17
 
16
- export { composeStoryAnnotations };
18
+ const isConditionalArgIncluded: (argType: InputType, args: Args, globals: Args) => boolean = includeConditionalArg;
19
+
20
+ export { composeStoryAnnotations, isConditionalArgIncluded };
17
21
 
18
22
  export { isExportStory, storyNameFromExport } from "storybook/internal/csf";