Skip to content

Commit

Permalink
Merge pull request #21650 from storybookjs/valentin/fix-disabled-addo…
Browse files Browse the repository at this point in the history
…n-interactions-issue

UI: Fix titles for addons/controls/panel tabs to support render fns and JSX elements
  • Loading branch information
valentinpalkovic authored Mar 22, 2023
2 parents 2f8fac5 + 4d07473 commit a1afb04
Show file tree
Hide file tree
Showing 16 changed files with 72 additions and 46 deletions.
1 change: 1 addition & 0 deletions code/addons/a11y/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ addons.register(ADDON_ID, (api) => {
const totalNb = violationsNb + incompleteNb;
return totalNb !== 0 ? `Accessibility (${totalNb})` : 'Accessibility';
},
id: 'accessibility',
type: types.PANEL,
render: ({ active = true, key }) => (
<A11yContextProvider key={key} active={active}>
Expand Down
50 changes: 32 additions & 18 deletions code/addons/actions/src/manager.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
import React, { useState, useEffect } from 'react';
import { addons, types } from '@storybook/manager-api';
import React, { useState } from 'react';
import { addons, types, useChannel } from '@storybook/manager-api';
import { STORY_CHANGED } from '@storybook/core-events';
import ActionLogger from './containers/ActionLogger';
import { ADDON_ID, EVENT_ID, PANEL_ID, PARAM_KEY } from './constants';

addons.register(ADDON_ID, (api) => {
addons.addPanel(PANEL_ID, {
title() {
const [actionsCount, setActionsCount] = useState(0);
const onEvent = () => setActionsCount((previous) => previous + 1);
const onChange = () => setActionsCount(0);
function Title({ count }: { count: { current: number } }) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const [_, setRerender] = useState(false);

useEffect(() => {
api.on(EVENT_ID, onEvent);
api.on(STORY_CHANGED, onChange);
return () => {
api.off(EVENT_ID, onEvent);
api.off(STORY_CHANGED, onChange);
};
});
const suffix = actionsCount === 0 ? '' : ` (${actionsCount})`;
return `Actions${suffix}`;
// Reactivity hack - force re-render on STORY_CHANGED and EVENT_ID events
useChannel({
[EVENT_ID]: () => {
setRerender((r) => !r);
},
[STORY_CHANGED]: () => {
setRerender((r) => !r);
},
});

const suffix = count.current === 0 ? '' : ` (${count.current})`;
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;
});

addons.addPanel(PANEL_ID, {
title: <Title count={countRef} />,
id: 'actions',
type: types.PANEL,
render: ({ active, key }) => <ActionLogger key={key} api={api} active={!!active} />,
paramKey: PARAM_KEY,
Expand Down
1 change: 1 addition & 0 deletions code/addons/backgrounds/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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
20 changes: 12 additions & 8 deletions code/addons/controls/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import { AddonPanel } from '@storybook/components';
import { ControlsPanel } from './ControlsPanel';
import { ADDON_ID, PARAM_KEY } from './constants';

function Title() {
const rows = useArgTypes();
const controlsCount = Object.values(rows).filter(
(argType) => argType?.control && !argType?.table?.disable
).length;
const suffix = controlsCount === 0 ? '' : ` (${controlsCount})`;

return <>Controls{suffix}</>;
}

addons.register(ADDON_ID, (api: API) => {
addons.addPanel(ADDON_ID, {
title() {
const rows = useArgTypes();
const controlsCount = Object.values(rows).filter(
(argType) => argType?.control && !argType?.table?.disable
).length;
const suffix = controlsCount === 0 ? '' : ` (${controlsCount})`;
return `Controls${suffix}`;
},
title: <Title />,
id: 'controls',
type: types.PANEL,
paramKey: PARAM_KEY,
render: ({ key, active }) => {
Expand Down
2 changes: 1 addition & 1 deletion code/addons/interactions/src/components/TabStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom';
import { StatusIcon } from './StatusIcon';

export const TabStatus = ({ children }: { children: React.ReactChild }) => {
const container = global.document.getElementById('tabbutton-interactions');
const container = global.document.getElementById('tabbutton-storybook-interactions-panel');
return container && ReactDOM.createPortal(children, container);
};

Expand Down
1 change: 1 addition & 0 deletions code/addons/jest/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Panel from './components/Panel';
addons.register(ADDON_ID, (api) => {
addons.addPanel(PANEL_ID, {
title: 'Tests',
id: 'tests',
render: ({ active, key }) => <Panel key={key} api={api} active={active} />,
paramKey: PARAM_KEY,
});
Expand Down
1 change: 1 addition & 0 deletions code/addons/measure/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Tool } from './Tool';
addons.register(ADDON_ID, () => {
addons.add(TOOL_ID, {
type: types.TOOL,
id: 'measure',
title: 'Measure',
match: ({ viewMode }) => viewMode === 'story',
render: () => <Tool />,
Expand Down
1 change: 1 addition & 0 deletions code/addons/outline/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { OutlineSelector } from './OutlineSelector';
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'Outline',
id: 'outline',
type: types.TOOL,
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
render: () => <OutlineSelector />,
Expand Down
1 change: 1 addition & 0 deletions code/addons/storysource/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ADDON_ID, PANEL_ID } from './index';
addons.register(ADDON_ID, (api) => {
addons.addPanel(PANEL_ID, {
title: 'Code',
id: 'code',
render: ({ active, key }) => (active ? <StoryPanel key={key} api={api} /> : null),
paramKey: 'storysource',
});
Expand Down
1 change: 1 addition & 0 deletions code/addons/toolbars/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ADDON_ID } from './constants';
addons.register(ADDON_ID, () =>
addons.add(ADDON_ID, {
title: ADDON_ID,
id: 'toolbar',
type: types.TOOL,
match: () => true,
render: () => <ToolbarManager />,
Expand Down
1 change: 1 addition & 0 deletions code/addons/viewport/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ViewportTool } from './Tool';
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'viewport / media-queries',
id: 'viewport',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => <ViewportTool />,
Expand Down
4 changes: 2 additions & 2 deletions code/e2e-tests/addon-interactions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test.describe('addon-interactions', () => {
const welcome = await sbPage.previewRoot().locator('.welcome');
await expect(welcome).toContainText('Welcome, Jane Doe!');

const interactionsTab = await page.locator('#tabbutton-interactions');
const interactionsTab = await page.locator('#tabbutton-storybook-interactions-panel');
await expect(interactionsTab).toContainText(/(1)/);
await expect(interactionsTab).toBeVisible();

Expand Down Expand Up @@ -59,7 +59,7 @@ test.describe('addon-interactions', () => {
const formInput = await sbPage.previewRoot().locator('#interaction-test-form input');
await expect(formInput).toHaveValue('final value');

const interactionsTab = await page.locator('#tabbutton-interactions');
const interactionsTab = await page.locator('#tabbutton-storybook-interactions-panel');
await expect(interactionsTab).toContainText(/(3)/);
await expect(interactionsTab).toBeVisible();

Expand Down
8 changes: 7 additions & 1 deletion code/lib/types/src/modules/addons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,14 @@ export interface Addon_RenderOptions {
key?: string;
}

export type ReactJSXElement = {
type: any;
props: any;
key: any;
};

export interface Addon_Type {
title: (() => string) | string;
title: (() => string) | string | ReactJSXElement;
type?: Addon_Types;
id?: string;
route?: (routeOptions: RouterData) => string;
Expand Down
15 changes: 3 additions & 12 deletions code/lib/types/src/modules/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ViewMode } from './csf';
import type { DocsOptions } from './core-common';
import type { API_HashEntry, API_IndexHash } from './api-stories';
import type { SetStoriesStory, SetStoriesStoryData } from './channelApi';
import type { Addon_Types } from './addons';
import type { Addon_Type } from './addons';
import type { StoryIndex } from './storyIndex';

export type API_ViewMode = 'story' | 'info' | 'settings' | 'page' | undefined | string;
Expand All @@ -30,17 +30,8 @@ export interface API_MatchOptions {
path: string;
}

export interface API_Addon {
title: string;
type?: Addon_Types;
id?: string;
route?: (routeOptions: API_RouteOptions) => string;
match?: (matchOptions: API_MatchOptions) => boolean;
render: (renderOptions: API_RenderOptions) => any;
paramKey?: string;
disabled?: boolean;
hidden?: boolean;
}
export type API_Addon = Addon_Type;

export interface API_Collection<T = API_Addon> {
[key: string]: T;
}
Expand Down
5 changes: 3 additions & 2 deletions code/ui/components/src/tabs/tabs.hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ export function useList(list: ChildrenList) {
/>
</AddonButton>
</WithTooltip>
{invisibleList.map(({ title, id, color }) => {
{invisibleList.map(({ title, id, color }, index) => {
const indexId = `index-${index}`;
return (
<TabButton
id={`tabbutton-${sanitize(title)}`}
id={`tabbutton-${sanitize(id) ?? indexId}`}
style={{ visibility: 'hidden' }}
aria-hidden
tabIndex={-1}
Expand Down
6 changes: 4 additions & 2 deletions code/ui/components/src/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,12 @@ export const Tabs: FC<TabsProps> = memo(
<Wrapper absolute={absolute} bordered={bordered} id={htmlId}>
<FlexBar scrollable={false} border backgroundColor={backgroundColor}>
<TabBar style={{ whiteSpace: 'normal' }} ref={tabBarRef} role="tablist">
{visibleList.map(({ title, id, active, color }) => {
{visibleList.map(({ title, id, active, color }, index) => {
const indexId = `index-${index}`;

return (
<TabButton
id={`tabbutton-${sanitize(title)}`}
id={`tabbutton-${sanitize(id) ?? indexId}`}
ref={(ref: HTMLButtonElement) => {
tabRefs.current.set(id, ref);
}}
Expand Down

0 comments on commit a1afb04

Please sign in to comment.