From 4b4943899be1b60cec94949e954bc12fdce91d0c Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 1 Nov 2024 11:25:24 +0100 Subject: [PATCH 1/3] Add capability for groups to TooltipLinkList, use it for main menu and update styling --- .../components/tooltip/ListItem.tsx | 1 + .../components/components/tooltip/Tooltip.tsx | 2 +- .../tooltip/TooltipLinkList.stories.tsx | 42 +++++++++++++++ .../components/tooltip/TooltipLinkList.tsx | 30 ++++++++--- .../src/manager/components/sidebar/Menu.tsx | 29 ++++++----- code/core/src/manager/container/Menu.tsx | 51 ++++++++++--------- 6 files changed, 111 insertions(+), 44 deletions(-) diff --git a/code/core/src/components/components/tooltip/ListItem.tsx b/code/core/src/components/components/tooltip/ListItem.tsx index 8d50a05273de..1752abeb1f8d 100644 --- a/code/core/src/components/components/tooltip/ListItem.tsx +++ b/code/core/src/components/components/tooltip/ListItem.tsx @@ -123,6 +123,7 @@ const Item = styled.div( ({ theme }) => ({ width: '100%', border: 'none', + borderRadius: theme.appBorderRadius, background: 'none', fontSize: theme.typography.size.s1, transition: 'all 150ms ease-out', diff --git a/code/core/src/components/components/tooltip/Tooltip.tsx b/code/core/src/components/components/tooltip/Tooltip.tsx index 77a6d4fc9c25..ef053f1baf5b 100644 --- a/code/core/src/components/components/tooltip/Tooltip.tsx +++ b/code/core/src/components/components/tooltip/Tooltip.tsx @@ -109,7 +109,7 @@ const Wrapper = styled.div( drop-shadow(0px 5px 5px rgba(0,0,0,0.05)) drop-shadow(0 1px 3px rgba(0,0,0,0.1)) `, - borderRadius: theme.appBorderRadius, + borderRadius: theme.appBorderRadius + 2, fontSize: theme.typography.size.s1, } : {} diff --git a/code/core/src/components/components/tooltip/TooltipLinkList.stories.tsx b/code/core/src/components/components/tooltip/TooltipLinkList.stories.tsx index 28952285756d..07e5ed7fc8d3 100644 --- a/code/core/src/components/components/tooltip/TooltipLinkList.stories.tsx +++ b/code/core/src/components/components/tooltip/TooltipLinkList.stories.tsx @@ -191,3 +191,45 @@ export const WithCustomIcon = { ], }, } satisfies Story; + +export const WithGroups = { + args: { + links: [ + [ + { + id: '1', + title: 'Link 1', + center: 'This is an addition description', + href: 'http://google.com', + onClick: onLinkClick, + }, + ], + [ + { + id: '1', + title: 'Link 1', + center: 'This is an addition description', + icon: , + href: 'http://google.com', + onClick: onLinkClick, + }, + { + id: '2', + title: 'Link 2', + center: 'This is an addition description', + href: 'http://google.com', + onClick: onLinkClick, + }, + ], + [ + { + id: '2', + title: 'Link 2', + center: 'This is an addition description', + href: 'http://google.com', + onClick: onLinkClick, + }, + ], + ], + }, +} satisfies Story; diff --git a/code/core/src/components/components/tooltip/TooltipLinkList.tsx b/code/core/src/components/components/tooltip/TooltipLinkList.tsx index f1467babec3a..eaeeba38f5a6 100644 --- a/code/core/src/components/components/tooltip/TooltipLinkList.tsx +++ b/code/core/src/components/components/tooltip/TooltipLinkList.tsx @@ -11,13 +11,20 @@ const List = styled.div( minWidth: 180, overflow: 'hidden', overflowY: 'auto', - maxHeight: 15.5 * 32, // 11.5 items + maxHeight: 15.5 * 32 + 8, // 15.5 items at 32px each + 8px padding }, ({ theme }) => ({ - borderRadius: theme.appBorderRadius, + borderRadius: theme.appBorderRadius + 2, }) ); +const Group = styled.div(({ theme }) => ({ + padding: 4, + '& + &': { + borderTop: `1px solid ${theme.appBorderColor}`, + }, +})); + export interface Link extends Omit { id: string; onClick?: ( @@ -42,17 +49,26 @@ const Item = ({ id, onClick, ...rest }: ItemProps) => { }; export interface TooltipLinkListProps extends ComponentProps { - links: Link[]; + links: Link[] | Link[][]; LinkWrapper?: LinkWrapperType; } export const TooltipLinkList = ({ links, LinkWrapper, ...props }: TooltipLinkListProps) => { - const isIndented = links.some((link) => link.icon); + const groups = Array.isArray(links[0]) ? (links as Link[][]) : [links as Link[]]; return ( - {links.map((link) => ( - - ))} + {groups + .filter((group) => group.length) + .map((group, index) => { + const isIndented = group.some((link) => link.icon); + return ( + link.id).join(`~${index}~`)}> + {group.map((link) => ( + + ))} + + ); + })} ); }; diff --git a/code/core/src/manager/components/sidebar/Menu.tsx b/code/core/src/manager/components/sidebar/Menu.tsx index d24656aa7079..583a2b982616 100644 --- a/code/core/src/manager/components/sidebar/Menu.tsx +++ b/code/core/src/manager/components/sidebar/Menu.tsx @@ -8,9 +8,10 @@ import { CloseIcon, CogIcon } from '@storybook/icons'; import { transparentize } from 'polished'; +import type { useMenu } from '../../container/Menu'; import { useLayout } from '../layout/LayoutProvider'; -export type MenuList = ComponentProps['links']; +export type MenuList = ReturnType; export const SidebarIconButton: FC & { highlighted: boolean }> = styled(IconButton)< @@ -60,17 +61,21 @@ const SidebarMenuList: FC<{ menu: MenuList; onHide: () => void; }> = ({ menu, onHide }) => { - const links = useMemo(() => { - return menu.map(({ onClick, ...rest }) => ({ - ...rest, - onClick: ((event, item) => { - if (onClick) { - onClick(event, item); - } - onHide(); - }) as ClickHandler, - })); - }, [menu, onHide]); + const links = useMemo( + () => + menu.map((group) => + group.map(({ onClick, ...rest }) => ({ + ...rest, + onClick: ((event, item) => { + if (onClick) { + onClick(event, item); + } + onHide(); + }) as ClickHandler, + })) + ), + [menu, onHide] + ); return ; }; diff --git a/code/core/src/manager/container/Menu.tsx b/code/core/src/manager/container/Menu.tsx index b2b92ef17ad1..fdf21a2ab9a1 100644 --- a/code/core/src/manager/container/Menu.tsx +++ b/code/core/src/manager/container/Menu.tsx @@ -9,6 +9,8 @@ import { STORIES_COLLAPSE_ALL } from '@storybook/core/core-events'; import type { API, State } from '@storybook/core/manager-api'; import { shortcutToHumanString } from '@storybook/core/manager-api'; +import type { Link } from '../../components/components/tooltip/TooltipLinkList'; + const focusableUIElements = { storySearchField: 'storybook-explorer-searchfield', storyListMenu: 'storybook-explorer-menu', @@ -58,8 +60,7 @@ export const useMenu = ( isPanelShown: boolean, isNavShown: boolean, enableShortcuts: boolean -) => { - const theme = useTheme(); +): Link[][] => { const shortcutKeys = api.getShortcutKeys(); const about = useMemo( @@ -105,11 +106,8 @@ export const useMenu = ( title: 'Keyboard shortcuts', onClick: () => api.changeSettingsTab('shortcuts'), right: enableShortcuts ? : null, - style: { - borderBottom: `4px solid ${theme.appBorderColor}`, - }, }), - [api, enableShortcuts, shortcutKeys.shortcutsPage, theme.appBorderColor] + [api, enableShortcuts, shortcutKeys.shortcutsPage] ); const sidebarToggle = useMemo( @@ -244,24 +242,29 @@ export const useMenu = ( }, [api, enableShortcuts, shortcutKeys]); return useMemo( - () => [ - about, - ...(state.whatsNewData?.status === 'SUCCESS' ? [whatsNew] : []), - documentation, - shortcuts, - sidebarToggle, - toolbarToogle, - addonsToggle, - addonsOrientationToggle, - fullscreenToggle, - searchToggle, - up, - down, - prev, - next, - collapse, - ...getAddonsShortcuts(), - ], + () => + [ + [ + about, + ...(state.whatsNewData?.status === 'SUCCESS' ? [whatsNew] : []), + documentation, + shortcuts, + ], + [ + sidebarToggle, + toolbarToogle, + addonsToggle, + addonsOrientationToggle, + fullscreenToggle, + searchToggle, + up, + down, + prev, + next, + collapse, + ], + getAddonsShortcuts(), + ] satisfies Link[][], [ about, state, From 18b471057f7610dbc75c861afaf432be44a5c4c5 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 1 Nov 2024 12:21:03 +0100 Subject: [PATCH 2/3] Indent all groups together --- code/core/src/components/components/tooltip/TooltipLinkList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/components/components/tooltip/TooltipLinkList.tsx b/code/core/src/components/components/tooltip/TooltipLinkList.tsx index eaeeba38f5a6..263c91d5bfa6 100644 --- a/code/core/src/components/components/tooltip/TooltipLinkList.tsx +++ b/code/core/src/components/components/tooltip/TooltipLinkList.tsx @@ -55,12 +55,12 @@ export interface TooltipLinkListProps extends ComponentProps { export const TooltipLinkList = ({ links, LinkWrapper, ...props }: TooltipLinkListProps) => { const groups = Array.isArray(links[0]) ? (links as Link[][]) : [links as Link[]]; + const isIndented = groups.some((group) => group.some((link) => link.icon)); return ( {groups .filter((group) => group.length) .map((group, index) => { - const isIndented = group.some((link) => link.icon); return ( link.id).join(`~${index}~`)}> {group.map((link) => ( From b02bc0aba8ec2c1405129c9c4cbd63babd32f84b Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 1 Nov 2024 13:02:46 +0100 Subject: [PATCH 3/3] Update TagsFilterPanel to use link groups --- .../components/sidebar/TagsFilterPanel.tsx | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/code/core/src/manager/components/sidebar/TagsFilterPanel.tsx b/code/core/src/manager/components/sidebar/TagsFilterPanel.tsx index f70fa6fca313..d9fc85c360d0 100644 --- a/code/core/src/manager/components/sidebar/TagsFilterPanel.tsx +++ b/code/core/src/manager/components/sidebar/TagsFilterPanel.tsx @@ -7,6 +7,8 @@ import type { Tag } from '@storybook/types'; import type { API } from '@storybook/core/manager-api'; +import type { Link } from '../../../components/components/tooltip/TooltipLinkList'; + const BUILT_IN_TAGS_SHOW = new Set(['play-fn']); const Wrapper = styled.div({ @@ -29,56 +31,60 @@ export const TagsFilterPanel = ({ toggleTag, isDevelopment, }: TagsFilterPanelProps) => { - const theme = useTheme(); const userTags = allTags.filter((tag) => !BUILT_IN_TAGS_SHOW.has(tag)); const docsUrl = api.getDocsUrl({ subpath: 'writing-stories/tags#filtering-by-custom-tags' }); - const items = allTags.map((tag) => { - const checked = selectedTags.includes(tag); - const id = `tag-${tag}`; - return { - id, - title: tag, - right: ( - { - // The onClick handler higher up the tree will handle the toggle - // For controlled inputs, a onClick handler is needed, though - // Accessibility-wise this isn't optimal, but I guess that's a limitation - // of the current design of TooltipLinkList - }} - /> - ), - onClick: () => toggleTag(tag), - }; - }) as any[]; + + const groups = [ + allTags.map((tag) => { + const checked = selectedTags.includes(tag); + const id = `tag-${tag}`; + return { + id, + title: tag, + right: ( + { + // The onClick handler higher up the tree will handle the toggle + // For controlled inputs, a onClick handler is needed, though + // Accessibility-wise this isn't optimal, but I guess that's a limitation + // of the current design of TooltipLinkList + }} + /> + ), + onClick: () => toggleTag(tag), + }; + }), + ] as Link[][]; if (allTags.length === 0) { - items.push({ - id: 'no-tags', - title: 'There are no tags. Use tags to organize and filter your Storybook.', - isIndented: false, - }); + groups.push([ + { + id: 'no-tags', + title: 'There are no tags. Use tags to organize and filter your Storybook.', + isIndented: false, + }, + ]); } + if (userTags.length === 0 && isDevelopment) { - items.push({ - id: 'tags-docs', - title: 'Learn how to add tags', - icon: , - href: docsUrl, - style: { - borderTop: `4px solid ${theme.appBorderColor}`, + groups.push([ + { + id: 'tags-docs', + title: 'Learn how to add tags', + icon: , + href: docsUrl, }, - }); + ]); } return ( - + ); };