From c47ca144a1429f446ac60ac51936ebca7bf40b03 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 11 Aug 2023 17:15:00 +0200 Subject: [PATCH 1/5] add the new type --- code/lib/manager-api/src/lib/addons.ts | 12 ++++++-- code/lib/types/src/modules/addons.ts | 41 ++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/code/lib/manager-api/src/lib/addons.ts b/code/lib/manager-api/src/lib/addons.ts index a7358b873cfb..1145ab1826f5 100644 --- a/code/lib/manager-api/src/lib/addons.ts +++ b/code/lib/manager-api/src/lib/addons.ts @@ -14,6 +14,7 @@ import type { Addon_TypesMapping, Addon_WrapperType, Addon_SidebarBottomType, + Addon_SidebarTopType, } from '@storybook/types'; import { Addon_TypesEnum } from '@storybook/types'; import { logger } from '@storybook/client-logger'; @@ -98,9 +99,13 @@ export class AddonStore { this.serverChannel = channel; }; - getElements( - type: T - ): Addon_Collection { + getElements< + T extends + | Addon_Types + | Addon_TypesEnum.experimental_PAGE + | Addon_TypesEnum.experimental_SIDEBAR_BOTTOM + | Addon_TypesEnum.experimental_SIDEBAR_TOP + >(type: T): Addon_Collection { if (!this.elements[type]) { this.elements[type] = {}; } @@ -142,6 +147,7 @@ export class AddonStore { id: string, addon: | Addon_BaseType + | (Omit & DeprecatedAddonWithId) | (Omit & DeprecatedAddonWithId) | (Omit & DeprecatedAddonWithId) | (Omit & DeprecatedAddonWithId) diff --git a/code/lib/types/src/modules/addons.ts b/code/lib/types/src/modules/addons.ts index 96c16ada1e79..e5ec1bb2c3f3 100644 --- a/code/lib/types/src/modules/addons.ts +++ b/code/lib/types/src/modules/addons.ts @@ -29,7 +29,12 @@ import type { } from './csf'; import type { IndexEntry } from './indexer'; -export type Addon_Types = Exclude; +export type Addon_Types = Exclude< + Addon_TypesEnum, + | Addon_TypesEnum.experimental_PAGE + | Addon_TypesEnum.experimental_SIDEBAR_BOTTOM + | Addon_TypesEnum.experimental_SIDEBAR_TOP +>; export interface Addon_ArgType extends InputType { defaultValue?: TArg; @@ -324,7 +329,12 @@ export type ReactJSXElement = { key: any; }; -export type Addon_Type = Addon_BaseType | Addon_PageType | Addon_WrapperType; +export type Addon_Type = + | Addon_BaseType + | Addon_PageType + | Addon_WrapperType + | Addon_SidebarBottomType + | Addon_SidebarTopType; export interface Addon_BaseType { /** * The title of the addon. @@ -335,7 +345,13 @@ export interface Addon_BaseType { * The type of the addon. * @example Addon_TypesEnum.PANEL */ - type: Exclude; + type: Exclude< + Addon_Types, + | Addon_TypesEnum.PREVIEW + | Addon_TypesEnum.experimental_PAGE + | Addon_TypesEnum.experimental_SIDEBAR_BOTTOM + | Addon_TypesEnum.experimental_SIDEBAR_TOP + >; /** * The unique id of the addon. * @warn This will become non-optional in 8.0 @@ -460,17 +476,31 @@ export interface Addon_SidebarBottomType { render: FCWithoutChildren; } +export interface Addon_SidebarTopType { + type: Addon_TypesEnum.experimental_SIDEBAR_TOP; + /** + * The unique id of the tool. + */ + id: string; + /** + * A React.FunctionComponent. + */ + render: FCWithoutChildren; +} + type Addon_TypeBaseNames = Exclude< Addon_TypesEnum, | Addon_TypesEnum.PREVIEW | Addon_TypesEnum.experimental_PAGE | Addon_TypesEnum.experimental_SIDEBAR_BOTTOM + | Addon_TypesEnum.experimental_SIDEBAR_TOP >; export interface Addon_TypesMapping extends Record { [Addon_TypesEnum.PREVIEW]: Addon_WrapperType; [Addon_TypesEnum.experimental_PAGE]: Addon_PageType; [Addon_TypesEnum.experimental_SIDEBAR_BOTTOM]: Addon_SidebarBottomType; + [Addon_TypesEnum.experimental_SIDEBAR_TOP]: Addon_SidebarTopType; } export type Addon_Loader = (api: API) => void; @@ -529,6 +559,11 @@ export enum Addon_TypesEnum { * @unstable */ experimental_SIDEBAR_BOTTOM = 'sidebar-bottom', + /** + * This adds items in the top of the sidebar. + * @unstable This will get replaced with a new API in 8.0, use at your own risk. + */ + experimental_SIDEBAR_TOP = 'sidebar-top', /** * @deprecated This property does nothing, and will be removed in Storybook 8.0. From ce7c1bf92ff17848c44979eba069e94012bb11e8 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 11 Aug 2023 17:30:32 +0200 Subject: [PATCH 2/5] inject the components into the sidebar top --- code/lib/manager-api/src/modules/addons.ts | 8 +++++++- code/ui/manager/src/components/sidebar/Heading.tsx | 6 ++++++ code/ui/manager/src/components/sidebar/Sidebar.tsx | 9 ++++++++- code/ui/manager/src/containers/sidebar.tsx | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/code/lib/manager-api/src/modules/addons.ts b/code/lib/manager-api/src/modules/addons.ts index 84fd51d7f206..0e1d69ca39e5 100644 --- a/code/lib/manager-api/src/modules/addons.ts +++ b/code/lib/manager-api/src/modules/addons.ts @@ -23,7 +23,13 @@ export interface SubAPI { * @param {Addon_Types | Addon_TypesEnum.experimental_PAGE} type - The type of the elements to retrieve. * @returns {API_Collection} - A collection of elements of the specified type. */ - getElements: ( + getElements: < + T extends + | Addon_Types + | Addon_TypesEnum.experimental_PAGE + | Addon_TypesEnum.experimental_SIDEBAR_BOTTOM + | Addon_TypesEnum.experimental_SIDEBAR_TOP = Addon_Types + >( type: T ) => Addon_Collection; /** diff --git a/code/ui/manager/src/components/sidebar/Heading.tsx b/code/ui/manager/src/components/sidebar/Heading.tsx index 8d11af4374f6..92ea6acebe89 100644 --- a/code/ui/manager/src/components/sidebar/Heading.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { styled } from '@storybook/theming'; import { Button } from '@storybook/components'; +import type { Addon_SidebarTopType } from '@storybook/types'; import { Brand } from './Brand'; import type { MenuList } from './Menu'; import { SidebarMenu } from './Menu'; @@ -10,6 +11,7 @@ import { SidebarMenu } from './Menu'; export interface HeadingProps { menuHighlighted?: boolean; menu: MenuList; + extra: Addon_SidebarTopType[]; skipLinkHref?: string; } @@ -73,6 +75,7 @@ export const Heading: FC> = menuHighlighted = false, menu, skipLinkHref, + extra, ...props }) => { return ( @@ -87,6 +90,9 @@ export const Heading: FC> = + {extra.map(({ id, render: Render }) => ( + + ))} ); diff --git a/code/ui/manager/src/components/sidebar/Sidebar.tsx b/code/ui/manager/src/components/sidebar/Sidebar.tsx index 7665fd41ae0a..9931b4d9260b 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.tsx @@ -4,7 +4,11 @@ import { styled } from '@storybook/theming'; import { ScrollArea, Spaced } from '@storybook/components'; import type { State } from '@storybook/manager-api'; -import type { Addon_SidebarBottomType, API_LoadedRefData } from '@storybook/types'; +import type { + Addon_SidebarBottomType, + Addon_SidebarTopType, + API_LoadedRefData, +} from '@storybook/types'; import { Heading } from './Heading'; // eslint-disable-next-line import/no-cycle @@ -98,6 +102,7 @@ export interface SidebarProps extends API_LoadedRefData { refs: State['refs']; status: State['status']; menu: any[]; + extra: Addon_SidebarTopType[]; bottom?: Addon_SidebarBottomType[]; storyId?: string; refId?: string; @@ -113,6 +118,7 @@ export const Sidebar = React.memo(function Sidebar({ status, previewInitialized, menu, + extra, bottom = [], menuHighlighted = false, enableShortcuts = true, @@ -131,6 +137,7 @@ export const Sidebar = React.memo(function Sidebar({ className="sidebar-header" menuHighlighted={menuHighlighted} menu={menu} + extra={extra} skipLinkHref="#storybook-preview-wrapper" /> diff --git a/code/ui/manager/src/containers/sidebar.tsx b/code/ui/manager/src/containers/sidebar.tsx index 9098b3bc99ab..23453945c2af 100755 --- a/code/ui/manager/src/containers/sidebar.tsx +++ b/code/ui/manager/src/containers/sidebar.tsx @@ -68,6 +68,7 @@ const Sidebar = React.memo(function Sideber() { const items = api.getElements(types.experimental_SIDEBAR_BOTTOM); const bottom = useMemo(() => Object.values(items), [items]); + const top = useMemo(() => Object.values(api.getElements(types.experimental_SIDEBAR_TOP)), []); return { title: name, @@ -84,6 +85,7 @@ const Sidebar = React.memo(function Sideber() { menuHighlighted: whatsNewNotificationsEnabled && api.isWhatsNewUnread(), enableShortcuts, bottom, + extra: top, }; }; return ( From f866f78678960214624db27ff6bde4f70f3f41b8 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 21 Aug 2023 14:02:18 +0200 Subject: [PATCH 3/5] force the brand area to not overflow the header --- code/ui/manager/src/components/sidebar/Heading.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ui/manager/src/components/sidebar/Heading.tsx b/code/ui/manager/src/components/sidebar/Heading.tsx index 92ea6acebe89..c9dd7862a572 100644 --- a/code/ui/manager/src/components/sidebar/Heading.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.tsx @@ -25,6 +25,9 @@ const BrandArea = styled.div(({ theme }) => ({ alignItems: 'center', minHeight: 22, + '& > * > *': { + maxWidth: '100%', + }, '& > *': { maxWidth: '100%', height: 'auto', From 809114e141070fa945a764372e60d540dc97e4e5 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 21 Aug 2023 14:09:24 +0200 Subject: [PATCH 4/5] fix stories --- .../components/sidebar/Heading.stories.tsx | 24 ++++++++++--------- .../src/components/sidebar/Heading.tsx | 6 ++--- .../components/sidebar/Sidebar.stories.tsx | 18 +++++++++++++- .../src/components/sidebar/Sidebar.tsx | 1 + 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/code/ui/manager/src/components/sidebar/Heading.stories.tsx b/code/ui/manager/src/components/sidebar/Heading.stories.tsx index d86cd02f9825..a79bb82d1e82 100644 --- a/code/ui/manager/src/components/sidebar/Heading.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.stories.tsx @@ -27,7 +27,9 @@ const menuItems = [ { title: 'Menu Item 3', onClick: action('onActivateMenuItem'), id: '3' }, ]; -export const MenuHighlighted: Story = () => ; +export const MenuHighlighted: Story = () => ( + +); export const standardData = { menu: menuItems }; @@ -45,7 +47,7 @@ export const Standard: Story = () => { }, }} > - + ); }; @@ -64,7 +66,7 @@ export const StandardNoLink: Story = () => { }, }} > - + ); }; @@ -83,7 +85,7 @@ export const LinkAndText: Story = () => { }, }} > - + ); }; @@ -102,7 +104,7 @@ export const OnlyText: Story = () => { }, }} > - + ); }; @@ -121,7 +123,7 @@ export const LongText: Story = () => { }, }} > - + ); }; @@ -140,7 +142,7 @@ export const CustomTitle: Story = () => { }, }} > - + ); }; @@ -159,7 +161,7 @@ export const CustomBrandImage: Story = () => { }, }} > - + ); }; @@ -178,7 +180,7 @@ export const CustomBrandImageTall: Story = () => { }, }} > - + ); }; @@ -197,7 +199,7 @@ export const CustomBrandImageUnsizedSVG: Story = () => { }, }} > - + ); }; @@ -216,7 +218,7 @@ export const NoBrand: Story = () => { }, }} > - + ); }; diff --git a/code/ui/manager/src/components/sidebar/Heading.tsx b/code/ui/manager/src/components/sidebar/Heading.tsx index c9dd7862a572..552e1859af69 100644 --- a/code/ui/manager/src/components/sidebar/Heading.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.tsx @@ -13,6 +13,7 @@ export interface HeadingProps { menu: MenuList; extra: Addon_SidebarTopType[]; skipLinkHref?: string; + isLoading: boolean; } const BrandArea = styled.div(({ theme }) => ({ @@ -79,6 +80,7 @@ export const Heading: FC> = menu, skipLinkHref, extra, + isLoading, ...props }) => { return ( @@ -93,9 +95,7 @@ export const Heading: FC> = - {extra.map(({ id, render: Render }) => ( - - ))} + {isLoading ? null : extra.map(({ id, render: Render }) => )} ); diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index 09dda0f61ec3..a1a266b55f34 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -62,6 +62,7 @@ export const Simple: Story = { ( - + ), }; @@ -86,6 +95,7 @@ export const Empty: Story = { Date: Mon, 21 Aug 2023 19:42:01 +0200 Subject: [PATCH 5/5] fixes --- code/ui/manager/src/components/layout/app.mockdata.tsx | 1 + .../manager/src/components/sidebar/Heading.stories.tsx | 7 ++++++- .../src/components/sidebar/__tests__/Sidebar.test.tsx | 10 +++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/code/ui/manager/src/components/layout/app.mockdata.tsx b/code/ui/manager/src/components/layout/app.mockdata.tsx index 1cb665508b7b..f41596303ed7 100644 --- a/code/ui/manager/src/components/layout/app.mockdata.tsx +++ b/code/ui/manager/src/components/layout/app.mockdata.tsx @@ -57,6 +57,7 @@ const realSidebarProps: SidebarProps = { refs: {}, status: {}, previewInitialized: true, + extra: [], }; const PlaceholderBlock = styled.div(({ color }) => ({ diff --git a/code/ui/manager/src/components/sidebar/Heading.stories.tsx b/code/ui/manager/src/components/sidebar/Heading.stories.tsx index a79bb82d1e82..b958ebe2e1b1 100644 --- a/code/ui/manager/src/components/sidebar/Heading.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.stories.tsx @@ -224,7 +224,12 @@ export const NoBrand: Story = () => { }; export const SkipToCanvasLinkFocused: ComponentStoryObj = { - args: { menu: menuItems, skipLinkHref: '#storybook-preview-wrapper' }, + args: { + menu: menuItems, + skipLinkHref: '#storybook-preview-wrapper', + extra: [], + isLoading: false, + }, parameters: { layout: 'padded', chromatic: { delay: 300 } }, play: () => { // focus each instance for chromatic/storybook's stacked theme diff --git a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx index 26fd8750f301..78ebfb697ac7 100644 --- a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx +++ b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx @@ -16,7 +16,15 @@ const factory = (props: Partial): RenderResult => { return render( - + ); };