diff --git a/code/addons/a11y/src/manager.test.tsx b/code/addons/a11y/src/manager.test.tsx index 0372c567f209..95d1dddb78b9 100644 --- a/code/addons/a11y/src/manager.test.tsx +++ b/code/addons/a11y/src/manager.test.tsx @@ -4,7 +4,7 @@ import './manager'; jest.mock('@storybook/manager-api'); const mockedApi = api as unknown as jest.Mocked; -mockedApi.getAddonState = jest.fn(); +mockedApi.useAddonState = jest.fn(); const mockedAddons = api.addons as jest.Mocked; const registrationImpl = mockedAddons.register.mock.calls[0][1]; @@ -29,25 +29,70 @@ 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; // when / then - expect(title()).toBe('Accessibility'); + expect(title()).toMatchInlineSnapshot(` +
+ + + Accessibility + + + +
+ `); }); 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; // when / then - expect(title()).toBe('Accessibility (3)'); + expect(title()).toMatchInlineSnapshot(` +
+ + + Accessibility + + + 3 + + +
+ `); }); }); diff --git a/code/addons/a11y/src/manager.tsx b/code/addons/a11y/src/manager.tsx index 718766ab07b8..927bdf2f3aba 100644 --- a/code/addons/a11y/src/manager.tsx +++ b/code/addons/a11y/src/manager.tsx @@ -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(ADDON_ID); + const violationsNb = addonState?.violations?.length || 0; + const incompleteNb = addonState?.incomplete?.length || 0; + const count = violationsNb + incompleteNb; + + const suffix = count === 0 ? '' : {count}; + + return ( +
+ + Accessibility + {suffix} + +
+ ); +}; + addons.register(ADDON_ID, (api) => { addons.add(PANEL_ID, { title: '', @@ -15,13 +34,7 @@ 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'; - }, + title: Title, type: types.PANEL, render: ({ active = true, key }) => ( diff --git a/code/addons/actions/src/manager.tsx b/code/addons/actions/src/manager.tsx index e7b81047231b..434e214de30a 100644 --- a/code/addons/actions/src/manager.tsx +++ b/code/addons/actions/src/manager.tsx @@ -1,47 +1,40 @@ -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 ? '' : {count}; + + return ( +
+ + Actions + {suffix} + +
+ ); } 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: Title, type: types.PANEL, render: ({ active, key }) => <ActionLogger key={key} api={api} active={!!active} />, paramKey: PARAM_KEY, diff --git a/code/addons/controls/src/manager.tsx b/code/addons/controls/src/manager.tsx index eee2ac03e7c5..de00635f7196 100644 --- a/code/addons/controls/src/manager.tsx +++ b/code/addons/controls/src/manager.tsx @@ -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'; @@ -9,14 +9,21 @@ 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 />, + title: Title, type: types.PANEL, paramKey: PARAM_KEY, render: ({ key, active }) => { diff --git a/code/addons/links/src/manager.ts b/code/addons/links/src/manager.ts index 9107c8c61904..e0c7487f2be9 100644 --- a/code/addons/links/src/manager.ts +++ b/code/addons/links/src/manager.ts @@ -3,9 +3,7 @@ import { addons } from '@storybook/manager-api'; import EVENTS, { ADDON_ID } from './constants'; addons.register(ADDON_ID, (api) => { - const channel = addons.getChannel(); - - channel.on(EVENTS.REQUEST, ({ kind, name }) => { + api.on(EVENTS.REQUEST, ({ kind, name }) => { const id = api.storyId(kind, name); api.emit(EVENTS.RECEIVE, id); });