@gtkx/storybook 2.0.0-beta.11 → 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 (51) hide show
  1. package/README.md +5 -5
  2. package/dist/actions-panel.d.ts.map +1 -1
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +2 -1
  5. package/dist/actions.js.map +1 -1
  6. package/dist/catalog.d.ts +1 -1
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +11 -5
  9. package/dist/catalog.js.map +1 -1
  10. package/dist/compose.d.ts.map +1 -1
  11. package/dist/compose.js.map +1 -1
  12. package/dist/config.d.ts +4 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js.map +1 -1
  15. package/dist/controls.d.ts +2 -1
  16. package/dist/controls.d.ts.map +1 -1
  17. package/dist/controls.js +30 -12
  18. package/dist/controls.js.map +1 -1
  19. package/dist/explorer-navigator.d.ts.map +1 -1
  20. package/dist/explorer-navigator.js +29 -2
  21. package/dist/explorer-navigator.js.map +1 -1
  22. package/dist/explorer-preview.d.ts.map +1 -1
  23. package/dist/explorer-preview.js +29 -13
  24. package/dist/explorer-preview.js.map +1 -1
  25. package/dist/explorer-view.d.ts +5 -1
  26. package/dist/explorer-view.d.ts.map +1 -1
  27. package/dist/explorer-view.js +17 -9
  28. package/dist/explorer-view.js.map +1 -1
  29. package/dist/explorer.d.ts +4 -0
  30. package/dist/explorer.d.ts.map +1 -1
  31. package/dist/explorer.js +6 -1
  32. package/dist/explorer.js.map +1 -1
  33. package/dist/types.d.ts +8 -2
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/dist/upstream.d.ts +4 -2
  37. package/dist/upstream.d.ts.map +1 -1
  38. package/dist/upstream.js +3 -1
  39. package/dist/upstream.js.map +1 -1
  40. package/package.json +14 -21
  41. package/src/actions.ts +28 -33
  42. package/src/catalog.ts +29 -19
  43. package/src/compose.ts +13 -7
  44. package/src/config.ts +4 -1
  45. package/src/controls.tsx +42 -18
  46. package/src/explorer-navigator.tsx +63 -28
  47. package/src/explorer-preview.tsx +95 -54
  48. package/src/explorer-view.tsx +56 -27
  49. package/src/explorer.ts +12 -1
  50. package/src/types.ts +42 -30
  51. package/src/upstream.ts +6 -8
@@ -4,12 +4,14 @@ import * as Gtk from "@gtkx/gi/gtk";
4
4
  import { AdwHeaderBar, AdwStatusPage, AdwToolbarView, AdwWindowTitle } from "@gtkx/jsx/adw";
5
5
  import { GtkBox, GtkButton, GtkScrolledWindow } from "@gtkx/jsx/gtk";
6
6
  import { createPortal, rootElement } from "@gtkx/react";
7
+ import { normalizeError } from "@gtkx/utils";
7
8
  import { Component, useCallback, useMemo, useState } from "react";
8
9
  import type { StoryEntry } from "./catalog.js";
9
- import type { Args, ComposedStory } from "./types.js";
10
+ import type { Args, ArgTypes, ComposedStory } from "./types.js";
10
11
  import { ActionsPanel } from "./actions-panel.js";
11
12
  import { ActionStore, bindActions } from "./actions.js";
12
13
  import { Controls } from "./controls.js";
14
+ import { isConditionalArgIncluded } from "./upstream.js";
13
15
 
14
16
  type PreviewProps = {
15
17
  entry: StoryEntry;
@@ -32,23 +34,25 @@ class PreviewBoundary extends Component<BoundaryProps, BoundaryState> {
32
34
  }
33
35
 
34
36
  static getDerivedStateFromError(error: unknown): Pick<BoundaryState, "error"> {
35
- return { error: error instanceof Error ? error : new Error(String(error)) };
37
+ return { error: normalizeError(error) };
36
38
  }
37
39
 
38
40
  override state: BoundaryState = { error: null, args: this.props.args };
39
41
 
40
42
  override render(): ReactNode {
41
- return this.state.error === null
42
- ? this.props.children
43
- : (
44
- <AdwStatusPage
45
- name="storybook-preview-error"
46
- title="Preview failed"
47
- iconName="dialog-error-symbolic"
48
- description={markupEscapeText(this.state.error.message.slice(0, 500), -1) +
49
- "\n\nAdjust controls, reset the preview, or select another story to continue."}
50
- />
51
- );
43
+ return this.state.error === null ? (
44
+ this.props.children
45
+ ) : (
46
+ <AdwStatusPage
47
+ name="storybook-preview-error"
48
+ title="Preview failed"
49
+ iconName="dialog-error-symbolic"
50
+ description={
51
+ markupEscapeText(this.state.error.message.slice(0, 500), -1) +
52
+ "\n\nAdjust controls, reset the preview, or select another story to continue."
53
+ }
54
+ />
55
+ );
52
56
  }
53
57
  }
54
58
 
@@ -63,6 +67,36 @@ const previewMode = (story: ComposedStory): "content" | "window" | "dialog" => {
63
67
  return mode;
64
68
  };
65
69
 
70
+ const mapValue = (value: unknown, mapping: Record<string, unknown>): unknown => {
71
+ const key = String(value);
72
+
73
+ return Object.hasOwn(mapping, key) ? mapping[key] : value;
74
+ };
75
+
76
+ const mapArgs = (args: Args, argTypes: ArgTypes): Args =>
77
+ Object.fromEntries(
78
+ Object.entries(args).map(([argument, value]) => {
79
+ const mapping = argTypes[argument]?.mapping;
80
+
81
+ if (mapping === undefined) {
82
+ return [argument, value];
83
+ }
84
+
85
+ return [
86
+ argument,
87
+ Array.isArray(value) ? value.map((item) => mapValue(item, mapping)) : mapValue(value, mapping),
88
+ ];
89
+ }),
90
+ );
91
+
92
+ const includedArgs = (args: Args, argTypes: ArgTypes, globals: Args): Args =>
93
+ Object.fromEntries(
94
+ Object.entries(args).map(([argument, value]) => [
95
+ argument,
96
+ isConditionalArgIncluded(argTypes[argument] ?? {}, args, globals) ? value : undefined,
97
+ ]),
98
+ );
99
+
66
100
  const PreviewContent = ({ story: Story, args }: { story: ComposedStory; args: Args }): ReactNode => {
67
101
  const mode = previewMode(Story);
68
102
 
@@ -83,20 +117,20 @@ const PreviewContent = ({ story: Story, args }: { story: ComposedStory; args: Ar
83
117
  const align = Story.parameters.layout === "centered" ? Gtk.Align.CENTER : Gtk.Align.FILL;
84
118
 
85
119
  return (
86
- <GtkBox
87
- name="storybook-canvas"
88
- orientation={Gtk.Orientation.VERTICAL}
89
- hexpand
90
- vexpand
91
- halign={align}
92
- valign={align}
93
- marginTop={margin}
94
- marginBottom={margin}
95
- marginStart={margin}
96
- marginEnd={margin}
97
- >
98
- <Story {...args} />
99
- </GtkBox>
120
+ <GtkScrolledWindow hexpand vexpand propagateNaturalWidth propagateNaturalHeight>
121
+ <GtkBox
122
+ name="storybook-canvas"
123
+ orientation={Gtk.Orientation.VERTICAL}
124
+ halign={align}
125
+ valign={align}
126
+ marginTop={margin}
127
+ marginBottom={margin}
128
+ marginStart={margin}
129
+ marginEnd={margin}
130
+ >
131
+ <Story {...args} />
132
+ </GtkBox>
133
+ </GtkScrolledWindow>
100
134
  );
101
135
  };
102
136
 
@@ -108,14 +142,24 @@ const usePreviewState = ({ entry }: PreviewProps) => {
108
142
  error: null,
109
143
  actions: new ActionStore(),
110
144
  }));
111
- const onError = useCallback((error: Error) => {
112
- setState((current) => current.story === state.story && current.generation === state.generation
113
- ? { ...current, error }
114
- : current);
115
- }, [state.story, state.generation]);
145
+ const onError = useCallback(
146
+ (error: Error) => {
147
+ setState((current) =>
148
+ current.story === state.story && current.generation === state.generation
149
+ ? { ...current, error }
150
+ : current,
151
+ );
152
+ },
153
+ [state.story, state.generation],
154
+ );
116
155
  const boundArgs = useMemo(
117
- () => bindActions(state.args, state.story.argTypes, state.actions, onError),
118
- [state.args, state.story.argTypes, state.actions, onError],
156
+ () =>
157
+ includedArgs(
158
+ bindActions(mapArgs(state.args, state.story.argTypes), state.story.argTypes, state.actions, onError),
159
+ state.story.argTypes,
160
+ state.story.globals,
161
+ ),
162
+ [state.args, state.story.argTypes, state.story.globals, state.actions, onError],
119
163
  );
120
164
 
121
165
  if (state.story !== entry.story) {
@@ -148,12 +192,7 @@ const usePreviewState = ({ entry }: PreviewProps) => {
148
192
  type InspectorProps = PreviewProps & { actions: ActionStore; args: Args; onChange: (args: Args) => void };
149
193
 
150
194
  const StoryInspector = ({ entry, actions, args, onChange }: InspectorProps): ReactNode => (
151
- <GtkScrolledWindow
152
- name="storybook-inspector"
153
- widthRequest={280}
154
- hscrollbarPolicy={Gtk.PolicyType.NEVER}
155
- vexpand
156
- >
195
+ <GtkScrolledWindow name="storybook-inspector" widthRequest={280} hscrollbarPolicy={Gtk.PolicyType.NEVER} vexpand>
157
196
  <GtkBox
158
197
  orientation={Gtk.Orientation.VERTICAL}
159
198
  spacing={24}
@@ -162,7 +201,7 @@ const StoryInspector = ({ entry, actions, args, onChange }: InspectorProps): Rea
162
201
  marginStart={12}
163
202
  marginEnd={12}
164
203
  >
165
- <Controls argTypes={entry.controls} args={args} onChange={onChange} />
204
+ <Controls argTypes={entry.controls} args={args} globals={entry.story.globals} onChange={onChange} />
166
205
  <ActionsPanel store={actions} />
167
206
  </GtkBox>
168
207
  </GtkScrolledWindow>
@@ -175,27 +214,29 @@ const StorybookPreview = ({ entry }: PreviewProps): ReactNode => {
175
214
  <AdwToolbarView
176
215
  hexpand
177
216
  vexpand
178
- topBar={(
217
+ top={
179
218
  <AdwHeaderBar
180
219
  titleWidget={<AdwWindowTitle title={entry.name} subtitle={entry.title} />}
181
220
  end={<GtkButton label="Reset story" onClicked={reset} />}
182
221
  />
183
- )}
222
+ }
184
223
  >
185
224
  <GtkBox>
186
225
  <GtkBox orientation={Gtk.Orientation.VERTICAL} hexpand vexpand>
187
226
  <PreviewBoundary key={state.generation} args={state.args}>
188
- {state.error === null
189
- ? <PreviewContent story={entry.story} args={boundArgs} />
190
- : (
191
- <AdwStatusPage
192
- name="storybook-preview-error"
193
- title="Story action failed"
194
- iconName="dialog-error-symbolic"
195
- description={markupEscapeText(state.error.message.slice(0, 500), -1) +
196
- "\n\nReset the preview or select another story to continue."}
197
- />
198
- )}
227
+ {state.error === null ? (
228
+ <PreviewContent story={entry.story} args={boundArgs} />
229
+ ) : (
230
+ <AdwStatusPage
231
+ name="storybook-preview-error"
232
+ title="Story action failed"
233
+ iconName="dialog-error-symbolic"
234
+ description={
235
+ markupEscapeText(state.error.message.slice(0, 500), -1) +
236
+ "\n\nReset the preview or select another story to continue."
237
+ }
238
+ />
239
+ )}
199
240
  </PreviewBoundary>
200
241
  </GtkBox>
201
242
  <StoryInspector entry={entry} actions={state.actions} args={state.args} onChange={onChange} />
@@ -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,56 +23,78 @@ 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
- sidebar={(
67
+ sidebar={
46
68
  <AdwToolbarView
47
- topBar={(
69
+ top={
48
70
  <AdwHeaderBar
49
- titleWidget={(
50
- <AdwWindowTitle title={title} subtitle={`${String(snapshot.stories.length)} stories`} />
51
- )}
71
+ titleWidget={
72
+ <AdwWindowTitle title={title} subtitle={storyCount(snapshot.stories.length)} />
73
+ }
52
74
  />
53
- )}
75
+ }
54
76
  >
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>
62
- )}
84
+ }
63
85
  >
64
- {selected
65
- ? <StorybookPreview key={selected.id} entry={selected} />
66
- : (
67
- <AdwToolbarView topBar={<AdwHeaderBar />}>
68
- <AdwStatusPage
69
- name="storybook-empty"
70
- title={snapshot.isLoading ? "Loading stories" : "Choose a story"}
71
- iconName="applications-development-symbolic"
72
- description="Stories from your project appear in the sidebar."
73
- />
74
- </AdwToolbarView>
75
- )}
86
+ {selected ? (
87
+ <StorybookPreview key={selected.id} entry={selected} />
88
+ ) : (
89
+ <AdwToolbarView top={<AdwHeaderBar />}>
90
+ <AdwStatusPage
91
+ name="storybook-empty"
92
+ title={snapshot.isLoading ? "Loading stories" : "Choose a story"}
93
+ iconName="applications-development-symbolic"
94
+ description="Stories from your project appear in the sidebar."
95
+ />
96
+ </AdwToolbarView>
97
+ )}
76
98
  </AdwOverlaySplitView>
77
99
  );
78
100
  };
@@ -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
@@ -71,20 +71,16 @@ type Annotations<TArgs extends object> = {
71
71
  };
72
72
 
73
73
  /** Default export metadata describing a component's stories and their shared annotations. */
74
- type ComponentMeta<TArgs extends object> = Annotations<TArgs> & Pick<
75
- ComponentAnnotations<NativeRenderer<TArgs>, TArgs>,
76
- "title" | "id" | "includeStories" | "excludeStories"
77
- > & {
78
- /** Component rendered with story args when no custom render annotation applies. */
79
- component?: ComponentType<TArgs>;
80
- /** Global overrides shared by this component's stories. */
81
- globals?: Args;
82
- };
74
+ type ComponentMeta<TArgs extends object> = Annotations<TArgs> &
75
+ Pick<ComponentAnnotations<NativeRenderer<TArgs>, TArgs>, "title" | "id" | "includeStories" | "excludeStories"> & {
76
+ /** Component rendered with story args when no custom render annotation applies. */
77
+ component?: ComponentType<TArgs>;
78
+ /** Global overrides shared by this component's stories. */
79
+ globals?: Args;
80
+ };
83
81
 
84
82
  /** Resolves a component's props or accepts an explicitly supplied args object type. */
85
- type ComponentOrProps<T> = T extends ComponentType<infer Props extends object>
86
- ? Props
87
- : T extends object ? T : Args;
83
+ type ComponentOrProps<T> = T extends ComponentType<infer Props extends object> ? Props : T extends object ? T : Args;
88
84
 
89
85
  /** Presents combined inferred args as a single object type. */
90
86
  type Simplify<T> = { [Key in keyof T]: T[Key] };
@@ -92,10 +88,12 @@ type Simplify<T> = { [Key in keyof T]: T[Key] };
92
88
  /** Infers story args from component props, render functions, decorators, or declared defaults. */
93
89
  type ArgsFrom<T> = T extends { component?: infer Component; render?: unknown; decorators?: unknown }
94
90
  ? Simplify<
95
- (NonNullable<Component> extends ComponentType<infer Props extends object> ? Props : object) &
96
- ArgsFromMeta<NativeRenderer, T>
97
- >
98
- : T extends { args: infer Defaults extends object } ? Defaults : Args;
91
+ (NonNullable<Component> extends ComponentType<infer Props extends object> ? Props : object) &
92
+ ArgsFromMeta<NativeRenderer, T>
93
+ >
94
+ : T extends { args: infer Defaults extends object }
95
+ ? Defaults
96
+ : Args;
99
97
 
100
98
  /** CSF3 default export metadata typed from a React component or an explicit args type. */
101
99
  type Meta<T = Args> = ComponentMeta<ComponentOrProps<T>>;
@@ -112,15 +110,16 @@ type StoryAnnotations<TArgs extends object> = Annotations<TArgs> & {
112
110
  type DefaultArgs<T> = T extends { args: infer Defaults } ? Defaults : Record<never, never>;
113
111
 
114
112
  /** Makes args with metadata defaults optional while preserving other required args. */
115
- type RequiredStoryArgs<T, TArgs extends object> =
116
- Omit<TArgs, keyof DefaultArgs<T>> & Partial<Pick<TArgs, keyof TArgs & keyof DefaultArgs<T>>>;
113
+ type RequiredStoryArgs<T, TArgs extends object> = Omit<TArgs, keyof DefaultArgs<T>> &
114
+ Partial<Pick<TArgs, keyof TArgs & keyof DefaultArgs<T>>>;
117
115
 
118
116
  /** CSF3 story object inferred from metadata, a React component, or an explicit args type. */
119
117
  type StoryObj<T = Args> = T extends { component?: unknown; render?: unknown; decorators?: unknown; args?: unknown }
120
- ? StoryAnnotations<ArgsFrom<T>> & Pick<
121
- StorybookAnnotations<NativeRenderer<ArgsFrom<T>>, ArgsFrom<T>, RequiredStoryArgs<T, ArgsFrom<T>>>,
122
- "args"
123
- >
118
+ ? StoryAnnotations<ArgsFrom<T>> &
119
+ Pick<
120
+ StorybookAnnotations<NativeRenderer<ArgsFrom<T>>, ArgsFrom<T>, RequiredStoryArgs<T, ArgsFrom<T>>>,
121
+ "args"
122
+ >
124
123
  : StoryAnnotations<ComponentOrProps<T>>;
125
124
 
126
125
  /** Project annotations shared by all composed stories. */
@@ -155,20 +154,33 @@ type StoryModule = {
155
154
  default: unknown;
156
155
  };
157
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
+
158
170
  /** Named module exports whose types match CSF3 stories, excluding module bookkeeping. */
159
171
  type StoryKeys<TModule extends StoryModule> = {
160
172
  [Key in keyof TModule]: Key extends "default" | "__esModule" | "__namedExportsOrder"
161
173
  ? never
162
- : TModule[Key] extends StoryAnnotations<ArgsFrom<TModule["default"]>>
163
- ? Key
164
- : never;
165
- }[keyof TModule] & string;
174
+ : [StoryArgs<TModule, TModule[Key]>] extends [never]
175
+ ? never
176
+ : Key;
177
+ }[keyof TModule] &
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,11 +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 {
3
- ComponentMeta,
4
- ComposedStory,
5
- Preview,
6
- StoryAnnotations,
7
- StoryRender,
8
- } from "./types.js";
4
+ import type { Args, ComponentMeta, ComposedStory, Preview, StoryAnnotations, StoryRender } from "./types.js";
9
5
 
10
6
  type ComposeStory = <TArgs extends object>(
11
7
  ...annotations: [
@@ -19,6 +15,8 @@ type ComposeStory = <TArgs extends object>(
19
15
 
20
16
  const composeStoryAnnotations = composeStory as ComposeStory;
21
17
 
22
- export { composeStoryAnnotations };
18
+ const isConditionalArgIncluded: (argType: InputType, args: Args, globals: Args) => boolean = includeConditionalArg;
19
+
20
+ export { composeStoryAnnotations, isConditionalArgIncluded };
23
21
 
24
22
  export { isExportStory, storyNameFromExport } from "storybook/internal/csf";