0
# Manager API
1
2
Experimental API for Storybook manager-side state management and UI customization. The Manager API provides access to stories, addons, global state, and UI elements within the Storybook manager interface.
3
4
**Note**: This API is experimental and may change in future versions. It's primarily intended for addon development and advanced customization scenarios.
5
6
## Capabilities
7
8
### Core Manager Hooks
9
10
Access to the main Storybook API and state management hooks.
11
12
```typescript { .api }
13
/**
14
* Get access to the Storybook API object with methods for controlling the manager
15
* @returns The Storybook API instance
16
*/
17
function useStorybookApi(): API;
18
19
/**
20
* Access the current Storybook state
21
* @returns The current state object
22
*/
23
function useStorybookState(): State;
24
25
/**
26
* Get access to the communication channel between manager and preview
27
* @returns Channel instance for cross-frame communication
28
*/
29
function useChannel(): Channel;
30
31
/**
32
* Access specific addon state
33
* @param addonId - The addon identifier
34
* @returns Addon-specific state
35
*/
36
function useAddonState<T>(addonId: string): [T, (newState: T) => void];
37
38
/**
39
* Access global parameters
40
* @returns Current global parameters
41
*/
42
function useGlobals(): [Args, (newGlobals: Args) => void];
43
```
44
45
**Usage Example:**
46
47
```typescript
48
import { useStorybookApi, useStorybookState } from "storybook/manager-api";
49
50
export const MyAddonPanel = () => {
51
const api = useStorybookApi();
52
const state = useStorybookState();
53
54
const handleStorySelect = (storyId: string) => {
55
api.selectStory(storyId);
56
};
57
58
return (
59
<div>
60
<h3>Current Story: {state.storyId}</h3>
61
<button onClick={() => handleStorySelect('example-button--primary')}>
62
Go to Primary Button
63
</button>
64
</div>
65
);
66
};
67
```
68
69
### Storybook API Interface
70
71
The main API object provides methods for controlling and querying the Storybook manager.
72
73
```typescript { .api }
74
interface API {
75
/** Navigate to a specific story */
76
selectStory: (storyId: string, viewMode?: ViewMode) => void;
77
78
/** Get current story data */
79
getCurrentStoryData: () => Story | undefined;
80
81
/** Get all stories */
82
getStories: () => StoriesHash;
83
84
/** Get story by ID */
85
getStory: (storyId: string) => Story | undefined;
86
87
/** Set global options */
88
setOptions: (options: Options) => void;
89
90
/** Add a panel to the addon panel area */
91
addPanel: (id: string, panel: Panel) => void;
92
93
/** Add a tool to the toolbar */
94
addToolbarItem: (id: string, item: ToolbarItem) => void;
95
96
/** Show/hide addon panel */
97
togglePanel: (panelId?: string) => void;
98
99
/** Toggle fullscreen mode */
100
toggleFullscreen: () => void;
101
102
/** Navigate to specific view mode */
103
setViewMode: (viewMode: ViewMode) => void;
104
105
/** Update URL without navigation */
106
setQueryParams: (params: QueryParams) => void;
107
108
/** Emit events to preview */
109
emit: (eventName: string, ...args: any[]) => void;
110
111
/** Listen to events from preview */
112
on: (eventName: string, handler: (...args: any[]) => void) => () => void;
113
114
/** Remove event listener */
115
off: (eventName: string, handler: (...args: any[]) => void) => void;
116
}
117
118
type ViewMode = 'story' | 'docs';
119
120
interface Options {
121
isFullscreen?: boolean;
122
showPanel?: boolean;
123
panelPosition?: 'bottom' | 'right';
124
showNav?: boolean;
125
showToolbar?: boolean;
126
initialActive?: string;
127
sidebar?: {
128
showRoots?: boolean;
129
collapsedRoots?: string[];
130
};
131
}
132
133
interface Panel {
134
title: string;
135
render: (options: { active: boolean; key: string }) => React.ReactNode;
136
paramKey?: string;
137
disabled?: boolean;
138
}
139
140
interface ToolbarItem {
141
title: string;
142
icon?: React.ReactNode;
143
onClick?: () => void;
144
render?: () => React.ReactNode;
145
disabled?: boolean;
146
}
147
```
148
149
### State Interface
150
151
The state object contains all current Storybook manager state.
152
153
```typescript { .api }
154
interface State {
155
/** Currently selected story ID */
156
storyId: string;
157
158
/** Current view mode */
159
viewMode: ViewMode;
160
161
/** All stories indexed by ID */
162
storiesHash: StoriesHash;
163
164
/** Stories configuration */
165
storiesConfigured: boolean;
166
167
/** Global parameters */
168
globals: Args;
169
170
/** Global types */
171
globalTypes: GlobalTypes;
172
173
/** UI state */
174
ui: {
175
name?: string;
176
url?: string;
177
enableShortcuts: boolean;
178
sidebarAnimations: boolean;
179
};
180
181
/** Layout state */
182
layout: {
183
isFullscreen: boolean;
184
showPanel: boolean;
185
panelPosition: 'bottom' | 'right';
186
showNav: boolean;
187
showToolbar: boolean;
188
};
189
190
/** Currently selected panel */
191
selectedPanel?: string;
192
193
/** Addon state */
194
addons: Record<string, any>;
195
}
196
197
interface StoriesHash {
198
[storyId: string]: Story | Group;
199
}
200
201
interface Group {
202
id: string;
203
name: string;
204
parent?: string;
205
depth: number;
206
children: string[];
207
isComponent: boolean;
208
isLeaf: boolean;
209
isRoot: boolean;
210
}
211
```
212
213
### Channel Communication
214
215
Communication channel for manager-preview messaging.
216
217
```typescript { .api }
218
interface Channel {
219
/** Emit event to preview */
220
emit: (eventName: string, ...args: any[]) => void;
221
222
/** Listen to events from preview */
223
on: (eventName: string, handler: (...args: any[]) => void) => () => void;
224
225
/** Remove event listener */
226
off: (eventName: string, handler: (...args: any[]) => void) => void;
227
228
/** Listen to event once */
229
once: (eventName: string, handler: (...args: any[]) => void) => () => void;
230
231
/** Remove all listeners for event */
232
removeAllListeners: (eventName?: string) => void;
233
}
234
235
// Common channel events
236
const STORY_CHANGED = 'storyChanged';
237
const STORY_RENDERED = 'storyRendered';
238
const STORY_THREW_EXCEPTION = 'storyThrewException';
239
const STORY_MISSING = 'storyMissing';
240
const SET_CURRENT_STORY = 'setCurrentStory';
241
const SELECT_STORY = 'selectStory';
242
const GLOBALS_UPDATED = 'globalsUpdated';
243
```
244
245
**Usage Example:**
246
247
```typescript
248
import { useChannel } from "storybook/manager-api";
249
250
export const MyAddon = () => {
251
const emit = useChannel({
252
[STORY_CHANGED]: (storyId) => {
253
console.log('Story changed to:', storyId);
254
},
255
[GLOBALS_UPDATED]: (globals) => {
256
console.log('Globals updated:', globals);
257
},
258
});
259
260
const handleTriggerAction = () => {
261
emit('myCustomEvent', { data: 'example' });
262
};
263
264
return (
265
<button onClick={handleTriggerAction}>
266
Trigger Custom Event
267
</button>
268
);
269
};
270
```
271
272
### Addon Development Utilities
273
274
Utilities specifically for developing Storybook addons.
275
276
```typescript { .api }
277
/**
278
* Register an addon panel
279
* @param addonId - Unique addon identifier
280
* @param panel - Panel configuration
281
*/
282
function addPanel(addonId: string, panel: Panel): void;
283
284
/**
285
* Register a toolbar item
286
* @param addonId - Unique addon identifier
287
* @param item - Toolbar item configuration
288
*/
289
function addToolbarItem(addonId: string, item: ToolbarItem): void;
290
291
/**
292
* Register addon decorator
293
* @param decorator - Decorator function
294
*/
295
function addDecorator(decorator: DecoratorFunction): void;
296
297
/**
298
* Add global types for addon parameters
299
* @param globalTypes - Global type definitions
300
*/
301
function addGlobalTypes(globalTypes: GlobalTypes): void;
302
303
interface GlobalTypes {
304
[key: string]: {
305
name: string;
306
description?: string;
307
defaultValue?: any;
308
toolbar?: {
309
title?: string;
310
icon?: string;
311
items: ToolbarMenuItems;
312
dynamicTitle?: boolean;
313
};
314
};
315
}
316
317
type ToolbarMenuItems = Array<{
318
value: string;
319
title: string;
320
left?: React.ReactNode;
321
right?: React.ReactNode;
322
}>;
323
```
324
325
**Usage Example:**
326
327
```typescript
328
import { addPanel, addToolbarItem } from "storybook/manager-api";
329
330
// Register addon panel
331
addPanel('my-addon/panel', {
332
title: 'My Addon',
333
render: ({ active }) => (
334
<div style={{ padding: 10 }}>
335
{active ? 'Panel is active!' : 'Panel is inactive'}
336
</div>
337
),
338
});
339
340
// Register toolbar item
341
addToolbarItem('my-addon/toolbar', {
342
title: 'My Tool',
343
icon: 'π§',
344
onClick: () => {
345
console.log('Toolbar item clicked');
346
},
347
});
348
```
349
350
## Advanced Usage Patterns
351
352
### Custom Addon State Management
353
354
```typescript
355
import { useAddonState, useChannel } from "storybook/manager-api";
356
357
interface MyAddonState {
358
enabled: boolean;
359
settings: Record<string, any>;
360
}
361
362
export const MyAddonManager = () => {
363
const [addonState, setAddonState] = useAddonState<MyAddonState>('my-addon', {
364
enabled: true,
365
settings: {},
366
});
367
368
const emit = useChannel({
369
'my-addon/settings-changed': (newSettings) => {
370
setAddonState({
371
...addonState,
372
settings: newSettings,
373
});
374
},
375
});
376
377
return (
378
<div>
379
<label>
380
<input
381
type="checkbox"
382
checked={addonState.enabled}
383
onChange={(e) =>
384
setAddonState({ ...addonState, enabled: e.target.checked })
385
}
386
/>
387
Enable Addon
388
</label>
389
</div>
390
);
391
};
392
```
393
394
### Story Navigation Helper
395
396
```typescript
397
import { useStorybookApi, useStorybookState } from "storybook/manager-api";
398
399
export const StoryNavigator = () => {
400
const api = useStorybookApi();
401
const state = useStorybookState();
402
403
const stories = Object.values(state.storiesHash).filter(
404
(item): item is Story => item.isLeaf
405
);
406
407
const currentIndex = stories.findIndex(story => story.id === state.storyId);
408
409
const goToNext = () => {
410
const nextIndex = (currentIndex + 1) % stories.length;
411
api.selectStory(stories[nextIndex].id);
412
};
413
414
const goToPrevious = () => {
415
const prevIndex = currentIndex === 0 ? stories.length - 1 : currentIndex - 1;
416
api.selectStory(stories[prevIndex].id);
417
};
418
419
return (
420
<div>
421
<button onClick={goToPrevious}>β Previous</button>
422
<span>{currentIndex + 1} of {stories.length}</span>
423
<button onClick={goToNext}>Next β</button>
424
</div>
425
);
426
};
427
```