Skip to content

Commit

Permalink
Merge pull request #23446 from storybookjs/release/7.2
Browse files Browse the repository at this point in the history
Features: For 7.2
  • Loading branch information
ndelangen authored Jul 19, 2023
2 parents c8e9caf + ac0f1de commit 26e4d5e
Show file tree
Hide file tree
Showing 321 changed files with 7,547 additions and 4,564 deletions.
27 changes: 27 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<h1>Migration</h1>

- [From version 7.0.0 to 7.2.0](#from-version-700-to-720)
- [Addon API are more type-strict](#addon-api-are-more-type-strict)
- [From version 6.5.x to 7.0.0](#from-version-65x-to-700)
- [7.0 breaking changes](#70-breaking-changes)
- [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below)
Expand Down Expand Up @@ -300,6 +302,31 @@
- [Packages renaming](#packages-renaming)
- [Deprecated embedded addons](#deprecated-embedded-addons)

## From version 7.0.0 to 7.2.0

#### Addon API is more type-strict

When registering an addon using `@storybook/manager-api`, the addon API is now more type-strict. This means if you use TypeScript to compile your addon before publishing, it might start giving you errors.

The `type` property is now a required field, and the `id` property should not be set anymore.

Here's a correct example:
```tsx
import { addons, types } from '@storybook/manager-api';

addons.register('my-addon', () => {
addons.add('my-addon/panel', {
type: types.PANEL,
title: 'My Addon',
render: ({ active }) => active ? <div>Hello World</div> : null,
});
});
```

The API: `addons.addPanel()` is now deprecated, and will be removed in 8.0.0. Please use `addons.add()` instead.

The `render` method can now be a `React.FunctionComponent` (without the `children` prop). Storybook will now render it, rather than calling it as a function.

## From version 6.5.x to 7.0.0

A number of these changes can be made automatically by the Storybook CLI. To take advantage of these "automigrations", run `npx storybook@latest upgrade --prerelease` or `pnpx dlx storybook@latest upgrade --prerelease`.
Expand Down
2 changes: 1 addition & 1 deletion code/addons/a11y/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
68 changes: 57 additions & 11 deletions code/addons/a11y/src/manager.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import * as api from '@storybook/manager-api';
import type { Addon_BaseType } from '@storybook/types';
import { PANEL_ID } from './constants';
import './manager';

jest.mock('@storybook/manager-api');
const mockedApi = api as unknown as jest.Mocked<api.API>;
mockedApi.getAddonState = jest.fn();
mockedApi.useAddonState = jest.fn();
const mockedAddons = api.addons as jest.Mocked<typeof api.addons>;
const registrationImpl = mockedAddons.register.mock.calls[0][1];

const isPanel = (input: Parameters<typeof mockedAddons.add>[1]): input is Addon_BaseType =>
input.type === api.types.PANEL;
describe('A11yManager', () => {
it('should register the panels', () => {
// when
Expand All @@ -29,25 +32,68 @@ describe('A11yManager', () => {

it('should compute title with no issues', () => {
// given
mockedApi.getAddonState.mockImplementation(() => undefined);
mockedApi.useAddonState.mockImplementation(() => [undefined]);
registrationImpl(api as unknown as api.API);
const title = mockedAddons.add.mock.calls
.map(([_, def]) => def)
.find(({ type }) => type === api.types.PANEL)?.title as Function;
const title = mockedAddons.add.mock.calls.map(([_, def]) => def).find(isPanel)
?.title as Function;

// when / then
expect(title()).toBe('Accessibility');
expect(title()).toMatchInlineSnapshot(`
<div>
<Spaced
col={1}
>
<span
style={
Object {
"display": "inline-block",
"verticalAlign": "middle",
}
}
>
Accessibility
</span>
</Spaced>
</div>
`);
});

it('should compute title with issues', () => {
// given
mockedApi.getAddonState.mockImplementation(() => ({ violations: [{}], incomplete: [{}, {}] }));
mockedApi.useAddonState.mockImplementation(() => [
{
violations: [{}],
incomplete: [{}, {}],
},
]);
registrationImpl(mockedApi);
const title = mockedAddons.add.mock.calls
.map(([_, def]) => def)
.find(({ type }) => type === api.types.PANEL)?.title as Function;
const title = mockedAddons.add.mock.calls.map(([_, def]) => def).find(isPanel)
?.title as Function;

// when / then
expect(title()).toBe('Accessibility (3)');
expect(title()).toMatchInlineSnapshot(`
<div>
<Spaced
col={1}
>
<span
style={
Object {
"display": "inline-block",
"verticalAlign": "middle",
}
}
>
Accessibility
</span>
<Badge
status="neutral"
>
3
</Badge>
</Spaced>
</div>
`);
});
});
34 changes: 23 additions & 11 deletions code/addons/a11y/src/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import React from 'react';
import { addons, types } from '@storybook/manager-api';
import { addons, types, useAddonState } from '@storybook/manager-api';
import { Badge, Spaced } from '@storybook/components';
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants';
import { VisionSimulator } from './components/VisionSimulator';
import { A11YPanel } from './components/A11YPanel';
import type { Results } from './components/A11yContext';
import { A11yContextProvider } from './components/A11yContext';

const Title = () => {
const [addonState] = useAddonState<Results>(ADDON_ID);
const violationsNb = addonState?.violations?.length || 0;
const incompleteNb = addonState?.incomplete?.length || 0;
const count = violationsNb + incompleteNb;

const suffix = count === 0 ? '' : <Badge status="neutral">{count}</Badge>;

return (
<div>
<Spaced col={1}>
<span style={{ display: 'inline-block', verticalAlign: 'middle' }}>Accessibility</span>
{suffix}
</Spaced>
</div>
);
};

addons.register(ADDON_ID, (api) => {
addons.add(PANEL_ID, {
title: '',
Expand All @@ -15,17 +34,10 @@ addons.register(ADDON_ID, (api) => {
});

addons.add(PANEL_ID, {
title() {
const addonState: Results = api?.getAddonState(ADDON_ID);
const violationsNb = addonState?.violations?.length || 0;
const incompleteNb = addonState?.incomplete?.length || 0;
const totalNb = violationsNb + incompleteNb;
return totalNb !== 0 ? `Accessibility (${totalNb})` : 'Accessibility';
},
id: 'accessibility',
title: Title,
type: types.PANEL,
render: ({ active = true, key }) => (
<A11yContextProvider key={key} active={active}>
render: ({ active = true }) => (
<A11yContextProvider active={active}>
<A11YPanel />
</A11yContextProvider>
),
Expand Down
1 change: 1 addition & 0 deletions code/addons/a11y/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["jest", "@testing-library/jest-dom"],
"skipLibCheck": true,
"strict": true
},
"include": ["src/**/*"]
Expand Down
2 changes: 1 addition & 1 deletion code/addons/actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
48 changes: 20 additions & 28 deletions code/addons/actions/src/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,42 @@
import React, { useState } from 'react';
import { addons, types, useChannel } from '@storybook/manager-api';
import React from 'react';
import { addons, types, useAddonState, useChannel } from '@storybook/manager-api';
import { STORY_CHANGED } from '@storybook/core-events';
import { Badge, Spaced } from '@storybook/components';
import ActionLogger from './containers/ActionLogger';
import { ADDON_ID, CLEAR_ID, EVENT_ID, PANEL_ID, PARAM_KEY } from './constants';

function Title({ count }: { count: { current: number } }) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const [_, setRerender] = useState(false);
function Title() {
const [{ count }, setCount] = useAddonState(ADDON_ID, { count: 0 });

// Reactivity hack - force re-render on STORY_CHANGED, EVENT_ID and CLEAR_ID events
useChannel({
[EVENT_ID]: () => {
setRerender((r) => !r);
setCount((c) => ({ ...c, count: c.count + 1 }));
},
[STORY_CHANGED]: () => {
setRerender((r) => !r);
setCount((c) => ({ ...c, count: 0 }));
},
[CLEAR_ID]: () => {
setRerender((r) => !r);
setCount((c) => ({ ...c, count: 0 }));
},
});

const suffix = count.current === 0 ? '' : ` (${count.current})`;
return <>Actions{suffix}</>;
const suffix = count === 0 ? '' : <Badge status="neutral">{count}</Badge>;

return (
<div>
<Spaced col={1}>
<span style={{ display: 'inline-block', verticalAlign: 'middle' }}>Actions</span>
{suffix}
</Spaced>
</div>
);
}

addons.register(ADDON_ID, (api) => {
const countRef = { current: 0 };

api.on(STORY_CHANGED, (id) => {
countRef.current = 0;
});

api.on(EVENT_ID, () => {
countRef.current += 1;
});

api.on(CLEAR_ID, () => {
countRef.current = 0;
});

addons.add(PANEL_ID, {
title: <Title count={countRef} />,
id: 'actions',
title: Title,
type: types.PANEL,
render: ({ active, key }) => <ActionLogger key={key} api={api} active={!!active} />,
render: ({ active }) => <ActionLogger api={api} active={!!active} />,
paramKey: PARAM_KEY,
});
});
2 changes: 1 addition & 1 deletion code/addons/backgrounds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
1 change: 0 additions & 1 deletion code/addons/backgrounds/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { GridSelector } from './containers/GridSelector';
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'Backgrounds',
id: 'backgrounds',
type: types.TOOL,
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
render: () => (
Expand Down
2 changes: 1 addition & 1 deletion code/addons/controls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
20 changes: 13 additions & 7 deletions code/addons/controls/src/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { addons, types, useArgTypes } from '@storybook/manager-api';
import { AddonPanel } from '@storybook/components';
import { AddonPanel, Badge, Spaced } from '@storybook/components';
import { ControlsPanel } from './ControlsPanel';
import { ADDON_ID, PARAM_KEY } from './constants';

Expand All @@ -9,23 +9,29 @@ function Title() {
const controlsCount = Object.values(rows).filter(
(argType) => argType?.control && !argType?.table?.disable
).length;
const suffix = controlsCount === 0 ? '' : ` (${controlsCount})`;
const suffix = controlsCount === 0 ? '' : <Badge status="neutral">{controlsCount}</Badge>;

return <>Controls{suffix}</>;
return (
<div>
<Spaced col={1}>
<span style={{ display: 'inline-block', verticalAlign: 'middle' }}>Controls</span>
{suffix}
</Spaced>
</div>
);
}

addons.register(ADDON_ID, (api) => {
addons.add(ADDON_ID, {
title: <Title />,
id: 'controls',
title: Title,
type: types.PANEL,
paramKey: PARAM_KEY,
render: ({ key, active }) => {
render: ({ active }) => {
if (!active || !api.getCurrentStoryData()) {
return null;
}
return (
<AddonPanel key={key} active={active}>
<AddonPanel active={active}>
<ControlsPanel />
</AddonPanel>
);
Expand Down
2 changes: 1 addition & 1 deletion code/addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/essentials/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/gfm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/highlight/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"*.d.ts"
],
"scripts": {
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
"check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
Expand Down
Loading

0 comments on commit 26e4d5e

Please sign in to comment.