or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdcli-commands.mdframework-support.mdhighlighting.mdindex.mdmanager-api.mdstory-composition.mdtesting.mdtheming.mdviewport.md

manager-api.mddocs/

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

```