diff --git a/code/ui/blocks/src/components/ArgsTable/Empty.tsx b/code/ui/blocks/src/components/ArgsTable/Empty.tsx index 7962d475acf8..ea8e3c2f17fc 100644 --- a/code/ui/blocks/src/components/ArgsTable/Empty.tsx +++ b/code/ui/blocks/src/components/ArgsTable/Empty.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import React, { useEffect, useState } from 'react'; import { styled } from '@storybook/theming'; -import { Icon, Link } from '@storybook/components/experimental'; +import { Link } from '@storybook/components/experimental'; interface EmptyProps { inAddonPanel?: boolean; @@ -53,16 +53,6 @@ const Divider = styled.div(({ theme }) => ({ backgroundColor: theme.appBorderColor, })); -const VideoIcon = styled.div(({ theme }) => ({ - width: 22, - height: 16, - borderRadius: theme.appBorderRadius, - border: `1px solid ${theme.color.secondary}`, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', -})); - export const Empty: FC = ({ inAddonPanel }) => { const [isLoading, setIsLoading] = useState(true); @@ -95,16 +85,7 @@ export const Empty: FC = ({ inAddonPanel }) => { {inAddonPanel && ( <> - - - - } - withArrow - > + Watch 5m video diff --git a/code/ui/components/src/experimental.ts b/code/ui/components/src/experimental.ts index 47764d881891..7280862f230f 100644 --- a/code/ui/components/src/experimental.ts +++ b/code/ui/components/src/experimental.ts @@ -12,3 +12,4 @@ export { Input } from './new/Input/Input'; export { Select } from './new/Select/Select'; export { Link } from './new/Link/Link'; export { Icon } from './new/Icon/Icon'; +export { IconButton } from './new/IconButton/IconButton'; diff --git a/code/ui/components/src/new/IconButton/IconButton.tsx b/code/ui/components/src/new/IconButton/IconButton.tsx index 9a0e1ad98ea9..ae20fa9e38ad 100644 --- a/code/ui/components/src/new/IconButton/IconButton.tsx +++ b/code/ui/components/src/new/IconButton/IconButton.tsx @@ -35,7 +35,7 @@ export const IconButton: { IconButton.displayName = 'IconButton'; const StyledButton = styled.button>( - ({ theme, variant = 'primary', size = 'medium', disabled = false, active = false }) => ({ + ({ theme, variant = 'solid', size = 'medium', disabled = false, active = false }) => ({ border: 0, cursor: disabled ? 'not-allowed' : 'pointer', display: 'inline-flex', @@ -64,41 +64,41 @@ const StyledButton = styled.button>( fontWeight: theme.typography.weight.bold, lineHeight: '1', background: `${(() => { - if (variant === 'primary') return theme.color.secondary; - if (variant === 'secondary') return theme.button.background; - if (variant === 'tertiary' && active) return theme.background.hoverable; + if (variant === 'solid') return theme.color.secondary; + if (variant === 'outline') return theme.button.background; + if (variant === 'ghost' && active) return theme.background.hoverable; return 'transparent'; })()}`, color: `${(() => { - if (variant === 'primary') return theme.color.lightest; - if (variant === 'secondary') return theme.input.color; - if (variant === 'tertiary' && active) return theme.color.secondary; - if (variant === 'tertiary') return theme.color.mediumdark; + if (variant === 'solid') return theme.color.lightest; + if (variant === 'outline') return theme.input.color; + if (variant === 'ghost' && active) return theme.color.secondary; + if (variant === 'ghost') return theme.color.mediumdark; return theme.input.color; })()}`, - boxShadow: variant === 'secondary' ? `${theme.button.border} 0 0 0 1px inset` : 'none', + boxShadow: variant === 'outline' ? `${theme.button.border} 0 0 0 1px inset` : 'none', borderRadius: theme.input.borderRadius, '&:hover': { - color: variant === 'tertiary' ? theme.color.secondary : null, + color: variant === 'ghost' ? theme.color.secondary : null, background: `${(() => { let bgColor = theme.color.secondary; - if (variant === 'primary') bgColor = theme.color.secondary; - if (variant === 'secondary') bgColor = theme.button.background; + if (variant === 'solid') bgColor = theme.color.secondary; + if (variant === 'outline') bgColor = theme.button.background; - if (variant === 'tertiary') return transparentize(0.86, theme.color.secondary); + if (variant === 'ghost') return transparentize(0.86, theme.color.secondary); return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor); })()}`, }, '&:active': { - color: variant === 'tertiary' ? theme.color.secondary : null, + color: variant === 'ghost' ? theme.color.secondary : null, background: `${(() => { let bgColor = theme.color.secondary; - if (variant === 'primary') bgColor = theme.color.secondary; - if (variant === 'secondary') bgColor = theme.button.background; + if (variant === 'solid') bgColor = theme.color.secondary; + if (variant === 'outline') bgColor = theme.button.background; - if (variant === 'tertiary') return theme.background.hoverable; + if (variant === 'ghost') return theme.background.hoverable; return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor); })()}`, }, diff --git a/code/ui/components/src/new/Link/Link.stories.tsx b/code/ui/components/src/new/Link/Link.stories.tsx index 356329f8e53c..0fee3e095b8a 100644 --- a/code/ui/components/src/new/Link/Link.stories.tsx +++ b/code/ui/components/src/new/Link/Link.stories.tsx @@ -1,7 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; - -import { Icon } from '@storybook/components/experimental'; import { Link } from './Link'; const meta: Meta = { @@ -26,24 +24,56 @@ export const Variants: Story = { Secondary - - Tertiary + + ), +}; + +export const Underline: Story = { + render: () => ( +
+ + Primary + + + Secondary + + + Secondary + + + Secondary
), }; -export const WithIcon: Story = { +export const Weight: Story = { render: () => (
- }> + Primary - }> + Secondary - }> - Tertiary + + Secondary + + + Secondary + +
+ ), +}; + +export const WithIcon: Story = { + render: () => ( +
+ + Primary + + + Secondary
), @@ -59,35 +89,14 @@ export const WithArrow: Story = { Secondary - - Tertiary -
- } - withArrow - > + Primary - } - withArrow - > + Secondary - } - withArrow - > - Tertiary -
), diff --git a/code/ui/components/src/new/Link/Link.tsx b/code/ui/components/src/new/Link/Link.tsx index a1da0f81733d..3422ac6a7f31 100644 --- a/code/ui/components/src/new/Link/Link.tsx +++ b/code/ui/components/src/new/Link/Link.tsx @@ -1,15 +1,17 @@ -import type { MouseEvent, ReactNode } from 'react'; +import type { MouseEvent } from 'react'; import React, { forwardRef } from 'react'; import { styled } from '@storybook/theming'; -import { darken } from 'polished'; +import type { Icons } from '@storybook/icons'; import { Icon } from '../Icon/Icon'; import type { PropsOf } from '../utils/types'; export interface LinkProps { as?: T; children: string; - variant?: 'primary' | 'secondary' | 'tertiary'; - icon?: ReactNode; + variant?: 'primary' | 'secondary'; + weight?: 'regular' | 'bold'; + underline?: 'hover' | 'always'; + icon?: Icons; onClick?: (e: MouseEvent) => void; withArrow?: boolean; } @@ -20,11 +22,12 @@ export const Link: { ): JSX.Element; displayName?: string; } = forwardRef( - ({ as, children, icon, withArrow, ...props }: LinkProps, ref: React.Ref) => { + ({ children, icon, withArrow, ...props }: LinkProps, ref: React.Ref) => { + const LocalIcon = Icon[icon]; return ( - + - {icon} + {icon && } {children} {withArrow && } @@ -35,39 +38,35 @@ export const Link: { Link.displayName = 'Link'; -const StyledLink = styled.a>(({ theme, variant = 'primary' }) => ({ - display: 'inline-flex', - gap: 4, - alignItems: 'center', - transition: 'all 150ms ease-out', - textDecoration: 'none', - lineHeight: 1, - color: `${(() => { - if (variant === 'primary') return theme.color.secondary; - if (variant === 'secondary') return theme.textMutedColor; - if (variant === 'tertiary') return theme.color.dark; - return theme.color.secondary; - })()}`, - - '&:hover, &:focus': { - cursor: 'pointer', +const StyledLink = styled.a>( + ({ theme, variant = 'primary', underline = 'hover', weight = 'regular' }) => ({ + display: 'inline-flex', + gap: 4, + alignItems: 'center', + transition: 'all 150ms ease-out', + textDecoration: 'none', + lineHeight: 1, color: `${(() => { - if (variant === 'primary') return darken(0.07, theme.color.secondary); - if (variant === 'secondary') return theme.color.dark; - if (variant === 'tertiary') return theme.color.darkest; - return darken(0.07, theme.color.secondary); + if (variant === 'primary') return theme.color.secondary; + if (variant === 'secondary') return theme.color.defaultText; + return theme.color.secondary; })()}`, - }, - - '&:active': { - color: `${(() => { - if (variant === 'primary') return darken(0.1, theme.color.secondary); - if (variant === 'secondary') return theme.color.darker; - if (variant === 'tertiary') return theme.textMutedColor; - return darken(0.1, theme.color.secondary); + fontWeight: `${(() => { + if (weight === 'regular') return theme.typography.weight.regular; + if (weight === 'bold') return theme.typography.weight.bold; + return theme.typography.weight.bold; })()}`, - }, -})); + textDecorationLine: `${underline === 'always' ? 'underline' : 'none'}`, + textDecorationStyle: 'solid', + textDecorationThickness: '1px', + textUnderlineOffset: '2px', + + '&:hover, &:focus': { + cursor: 'pointer', + textDecorationLine: 'underline', + }, + }) +); const StyledLeft = styled.span(({ theme }) => ({ display: 'inline-flex', diff --git a/code/ui/manager/src/globals/exports.ts b/code/ui/manager/src/globals/exports.ts index ffd247fdd30d..6ad6218c0fff 100644 --- a/code/ui/manager/src/globals/exports.ts +++ b/code/ui/manager/src/globals/exports.ts @@ -114,7 +114,7 @@ export default { 'resetComponents', 'withReset', ], - '@storybook/components/experimental': ['Button', 'Icon', 'Input', 'Link', 'Select'], + '@storybook/components/experimental': ['Button', 'Icon', 'IconButton', 'Input', 'Link', 'Select'], '@storybook/channels': [ 'Channel', 'PostMessageTransport',