Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Menu design upgrade #20898

Merged
merged 2 commits into from Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion code/addons/a11y/src/components/VisionSimulator.tsx
Expand Up @@ -133,7 +133,6 @@ export const VisionSimulator = () => {
)}
<WithTooltip
placement="top"
trigger="click"
tooltip={({ onHide }) => {
const colorList = getColorList(filter, (i) => {
setFilter(i);
Expand Down
Expand Up @@ -116,7 +116,6 @@ export const BackgroundSelector: FC = memo(function BackgroundSelector() {
<Fragment>
<WithTooltip
placement="top"
trigger="click"
closeOnOutsideClick
tooltip={({ onHide }) => {
return (
Expand Down
1 change: 0 additions & 1 deletion code/addons/toolbars/src/components/ToolbarMenuList.tsx
Expand Up @@ -57,7 +57,6 @@ export const ToolbarMenuList: FC<ToolbarMenuListProps> = withKeyboardCycle(
return (
<WithTooltip
placement="top"
trigger="click"
tooltip={({ onHide }) => {
const links = items
// Special case handling for various "type" variants
Expand Down
39 changes: 7 additions & 32 deletions code/addons/toolbars/src/components/ToolbarMenuListItem.tsx
@@ -1,18 +1,9 @@
import type { ReactNode } from 'react';
import React from 'react';
import type { TooltipLinkListLink } from '@storybook/components';
import { Icons } from '@storybook/components';
import type { ToolbarItem } from '../types';

interface ListItem {
id: string;
left?: ReactNode;
title?: ReactNode;
right?: ReactNode;
active?: boolean;
onClick?: () => void;
}

type ToolbarMenuListItemProps = {
export type ToolbarMenuListItemProps = {
currentValue: string;
onClick: () => void;
} & ToolbarItem;
Expand All @@ -28,34 +19,18 @@ export const ToolbarMenuListItem = ({
currentValue,
}: ToolbarMenuListItemProps) => {
const Icon = icon && <Icons style={{ opacity: 1 }} icon={icon} />;
const hasContent = left || right || title;

const Item: ListItem = {
const Item: TooltipLinkListLink = {
id: value || currentValue,
active: currentValue === value,
right,
title,
left,
onClick,
};

if (left) {
Item.left = left;
}

if (right) {
Item.right = right;
}

if (title) {
Item.title = title;
}

if (icon && !hideIcon) {
if (hasContent && !right) {
Item.right = Icon;
} else if (hasContent && !left) {
Item.left = Icon;
} else if (!hasContent) {
Item.right = Icon;
}
Item.left = Icon;
}

return Item;
Expand Down
1 change: 0 additions & 1 deletion code/addons/viewport/src/Tool.tsx
Expand Up @@ -180,7 +180,6 @@ export const ViewportTool: FC = memo(
<Fragment>
<WithTooltip
placement="top"
trigger="click"
tooltip={({ onHide }) => (
<TooltipLinkList links={toLinks(list, item, setState, state, onHide)} />
)}
Expand Down
1 change: 0 additions & 1 deletion code/ui/blocks/src/components/ArgsTable/ArgValue.tsx
Expand Up @@ -161,7 +161,6 @@ const ArgSummary: FC<ArgSummaryProps> = ({ value, initialExpandedArgs }) => {
return (
<WithTooltipPure
closeOnOutsideClick
trigger="click"
placement="bottom"
visible={isOpen}
onVisibleChange={(isVisible) => {
Expand Down
1 change: 0 additions & 1 deletion code/ui/blocks/src/controls/Color.tsx
Expand Up @@ -320,7 +320,6 @@ export const ColorControl: FC<ColorControlProps> = ({
return (
<Wrapper>
<PickerTooltip
trigger="click"
startOpen={startOpen}
closeOnOutsideClick
onVisibleChange={() => addPreset(color)}
Expand Down
4 changes: 3 additions & 1 deletion code/ui/components/src/index.ts
Expand Up @@ -56,7 +56,8 @@ export { Form } from './form/index';
export { WithTooltip, WithTooltipPure } from './tooltip/lazy-WithTooltip';
export { TooltipMessage } from './tooltip/TooltipMessage';
export { TooltipNote } from './tooltip/TooltipNote';
export { TooltipLinkList } from './tooltip/TooltipLinkList';
export { TooltipLinkList, type Link as TooltipLinkListLink } from './tooltip/TooltipLinkList';
export { default as ListItem } from './tooltip/ListItem';

// Toolbar and subcomponents
export { Tabs, TabsState, TabBar, TabWrapper } from './tabs/tabs';
Expand All @@ -68,6 +69,7 @@ export { AddonPanel } from './addon-panel/addon-panel';
// Graphics
export type { IconsProps } from './icon/icon';
export { Icons, Symbols } from './icon/icon';
export { icons } from './icon/icons';
export { StorybookLogo } from './brand/StorybookLogo';
export { StorybookIcon } from './brand/StorybookIcon';

Expand Down
1 change: 0 additions & 1 deletion code/ui/components/src/tabs/tabs.hooks.tsx
Expand Up @@ -60,7 +60,6 @@ export function useList(list: ChildrenList) {
<>
<WithTooltip
interactive
withArrows={false}
visible={isTooltipVisible}
onVisibleChange={setTooltipVisible}
placement="bottom"
Expand Down
23 changes: 19 additions & 4 deletions code/ui/components/src/tabs/tabs.stories.tsx
Expand Up @@ -4,7 +4,14 @@ import React, { Fragment } from 'react';
import { action } from '@storybook/addon-actions';
import { logger } from '@storybook/client-logger';
import type { Meta, StoryObj } from '@storybook/react';
import { within, fireEvent, waitFor, screen, getByText } from '@storybook/testing-library';
import {
within,
fireEvent,
waitFor,
screen,
getByText,
userEvent,
} from '@storybook/testing-library';
import { Tabs, TabsState, TabWrapper } from './tabs';

const colours = Array.from(new Array(15), (val, index) => index).map((i) =>
Expand Down Expand Up @@ -220,10 +227,18 @@ export const StatefulDynamicWithOpenTooltip = {
const addonsTab = await canvas.findByRole('tab', { name: /Addons/ });

await waitFor(async () => {
await fireEvent(addonsTab, new MouseEvent('mouseenter', { bubbles: true }));
const tooltip = await screen.getByTestId('tooltip');
await expect(tooltip).toBeInTheDocument();
const tooltip = await screen.queryByTestId('tooltip');

if (!tooltip) {
await userEvent.click(addonsTab);
}

if (!tooltip) {
throw new Error('Tooltip not found');
}
});

expect(screen.queryByTestId('tooltip')).toBeInTheDocument();
},
render: (args) => (
<TabsState initial="test1" {...args}>
Expand Down
111 changes: 59 additions & 52 deletions code/ui/components/src/tooltip/ListItem.tsx
@@ -1,8 +1,10 @@
import type { FC, ReactNode, ComponentProps } from 'react';
import type { FC, ReactNode, ComponentProps, ReactElement } from 'react';
import React from 'react';
import { styled } from '@storybook/theming';
import memoize from 'memoizerific';
import { transparentize } from 'polished';
import { Icons } from '../icon/icon';
import { icons } from '../icon/icons';

export interface TitleProps {
children?: ReactNode;
Expand Down Expand Up @@ -47,53 +49,39 @@ export interface RightProps {
active?: boolean;
}

const Right = styled.span<RightProps>(
{
'& svg': {
transition: 'all 200ms ease-out',
opacity: 0,
height: 12,
width: 12,
margin: '3px 0',
verticalAlign: 'top',
},
'& path': {
fill: 'inherit',
},
const Right = styled.span<RightProps>({
display: 'flex',
'& svg': {
height: 12,
width: 12,
margin: '3px 0',
verticalAlign: 'top',
},
({ active, theme }) =>
active
? {
'& svg': {
opacity: 1,
},
'& path': {
fill: theme.color.secondary,
},
}
: {}
);

const Center = styled.span({
flex: 1,
textAlign: 'left',
display: 'inline-flex',

'& > * + *': {
paddingLeft: 10,
'& path': {
fill: 'inherit',
},
});

const Center = styled.span<{ isIndented: boolean }>(
{
flex: 1,
textAlign: 'left',
display: 'flex',
flexDirection: 'column',
},
({ isIndented }) => (isIndented ? { marginLeft: 24 } : {})
);

export interface CenterTextProps {
active?: boolean;
disabled?: boolean;
}

const CenterText = styled.span<CenterTextProps>(
{
flex: 1,
textAlign: 'center',
},
({ theme }) => ({
fontSize: '11px',
lineHeight: '14px',
}),
({ active, theme }) =>
active
? {
Expand All @@ -112,17 +100,22 @@ export interface LeftProps {
active?: boolean;
}

const Left = styled.span<LeftProps>(({ active, theme }) =>
active
? {
'& svg': {
opacity: 1,
},
'& path': {
fill: theme.color.primary,
},
}
: {}
const Left = styled.span<LeftProps>(
({ active, theme }) =>
active
? {
'& svg': {
opacity: 1,
},
'& svg path': {
fill: theme.color.secondary,
},
}
: {},
() => ({
display: 'flex',
maxWidth: 14,
})
);

export interface ItemProps {
Expand All @@ -139,7 +132,7 @@ const Item = styled.a<ItemProps>(
justifyContent: 'space-between',

lineHeight: '18px',
padding: '7px 15px',
padding: '7px 10px',
display: 'flex',
alignItems: 'center',

Expand Down Expand Up @@ -188,14 +181,20 @@ export type LinkWrapperType = FC<any>;

export interface ListItemProps extends Omit<ComponentProps<typeof Item>, 'href' | 'title'> {
loading?: boolean;
/**
* @deprecated This property will be removed in SB 8.0
* Use `icon` property instead.
*/
left?: ReactNode;
title?: ReactNode;
center?: ReactNode;
right?: ReactNode;
icon?: keyof typeof icons | ReactElement;
active?: boolean;
disabled?: boolean;
href?: string;
LinkWrapper?: LinkWrapperType;
isIndented?: boolean;
}

const ListItem: FC<ListItemProps> = ({
Expand All @@ -204,8 +203,10 @@ const ListItem: FC<ListItemProps> = ({
title,
center,
right,
icon,
active,
disabled,
isIndented,
href,
onClick,
LinkWrapper,
Expand All @@ -214,11 +215,17 @@ const ListItem: FC<ListItemProps> = ({
const itemProps = getItemProps(onClick, href, LinkWrapper);
const commonProps = { active, disabled };

const isStorybookIcon = typeof icon === 'string' && icons[icon];

return (
<Item {...commonProps} {...rest} {...itemProps}>
{left && <Left {...commonProps}>{left}</Left>}
{icon ? (
<Left {...commonProps}>{isStorybookIcon ? <Icons icon={icon} /> : icon}</Left>
) : (
left && <Left {...commonProps}>{left}</Left>
)}
{title || center ? (
<Center>
<Center isIndented={!left && !icon && isIndented}>
{title && (
<Title {...commonProps} loading={loading}>
{title}
Expand Down
4 changes: 2 additions & 2 deletions code/ui/components/src/tooltip/Tooltip.tsx
Expand Up @@ -108,7 +108,7 @@ const Wrapper = styled.div<WrapperProps>(
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 * 2,
borderRadius: theme.appBorderRadius,
fontSize: theme.typography.size.s1,
}
: {}
Expand All @@ -126,7 +126,7 @@ export interface TooltipProps {

export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
(
{ placement, hasChrome, children, arrowProps, tooltipRef, color, withArrows = true, ...props },
{ placement, hasChrome, children, arrowProps, tooltipRef, color, withArrows, ...props },
ref
) => {
return (
Expand Down