Skip to content

Commit

Permalink
Merge pull request #23767 from storybookjs/charles-improve-link-compo…
Browse files Browse the repository at this point in the history
…nent

UI: Improve Link component
  • Loading branch information
kasperpeulen committed Aug 9, 2023
2 parents a4c6298 + 0362536 commit fbbbf14
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 108 deletions.
23 changes: 2 additions & 21 deletions code/ui/blocks/src/components/ArgsTable/Empty.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<EmptyProps> = ({ inAddonPanel }) => {
const [isLoading, setIsLoading] = useState(true);

Expand Down Expand Up @@ -95,16 +85,7 @@ export const Empty: FC<EmptyProps> = ({ inAddonPanel }) => {
<Links>
{inAddonPanel && (
<>
<Link
href="https://youtu.be/0gOfS6K0x0E"
target="_blank"
icon={
<VideoIcon>
<Icon.Play size={8} />
</VideoIcon>
}
withArrow
>
<Link href="https://youtu.be/0gOfS6K0x0E" target="_blank" icon="Video" withArrow>
Watch 5m video
</Link>
<Divider />
Expand Down
1 change: 1 addition & 0 deletions code/ui/components/src/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
34 changes: 17 additions & 17 deletions code/ui/components/src/new/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const IconButton: {
IconButton.displayName = 'IconButton';

const StyledButton = styled.button<Omit<ButtonProps, 'icon'>>(
({ 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',
Expand Down Expand Up @@ -64,41 +64,41 @@ const StyledButton = styled.button<Omit<ButtonProps, 'icon'>>(
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);
})()}`,
},
Expand Down
73 changes: 41 additions & 32 deletions code/ui/components/src/new/Link/Link.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof Link> = {
Expand All @@ -26,24 +24,56 @@ export const Variants: Story = {
<Link href="https://storybook.js.org/" variant="secondary">
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="tertiary">
Tertiary
</div>
),
};

export const Underline: Story = {
render: () => (
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<Link href="https://storybook.js.org/" variant="primary" underline="hover">
Primary
</Link>
<Link href="https://storybook.js.org/" variant="primary" underline="always">
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="secondary" underline="hover">
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="secondary" underline="always">
Secondary
</Link>
</div>
),
};

export const WithIcon: Story = {
export const Weight: Story = {
render: () => (
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<Link href="https://storybook.js.org/" variant="primary" icon={<Icon.FaceHappy />}>
<Link href="https://storybook.js.org/" variant="primary" weight="regular">
Primary
</Link>
<Link href="https://storybook.js.org/" variant="secondary" icon={<Icon.FaceHappy />}>
<Link href="https://storybook.js.org/" variant="primary" weight="bold">
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="tertiary" icon={<Icon.FaceHappy />}>
Tertiary
<Link href="https://storybook.js.org/" variant="secondary" weight="regular">
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="secondary" weight="bold">
Secondary
</Link>
</div>
),
};

export const WithIcon: Story = {
render: () => (
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<Link href="https://storybook.js.org/" variant="primary" icon="FaceHappy">
Primary
</Link>
<Link href="https://storybook.js.org/" variant="secondary" icon="FaceHappy">
Secondary
</Link>
</div>
),
Expand All @@ -59,35 +89,14 @@ export const WithArrow: Story = {
<Link href="https://storybook.js.org/" variant="secondary" withArrow>
Secondary
</Link>
<Link href="https://storybook.js.org/" variant="tertiary" withArrow>
Tertiary
</Link>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<Link
href="https://storybook.js.org/"
variant="primary"
icon={<Icon.FaceHappy />}
withArrow
>
<Link href="https://storybook.js.org/" variant="primary" icon="FaceHappy" withArrow>
Primary
</Link>
<Link
href="https://storybook.js.org/"
variant="secondary"
icon={<Icon.FaceHappy />}
withArrow
>
<Link href="https://storybook.js.org/" variant="secondary" icon="FaceHappy" withArrow>
Secondary
</Link>
<Link
href="https://storybook.js.org/"
variant="tertiary"
icon={<Icon.FaceHappy />}
withArrow
>
Tertiary
</Link>
</div>
</div>
),
Expand Down
73 changes: 36 additions & 37 deletions code/ui/components/src/new/Link/Link.tsx
Original file line number Diff line number Diff line change
@@ -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<T extends React.ElementType = React.ElementType> {
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;
}
Expand All @@ -20,11 +22,12 @@ export const Link: {
): JSX.Element;
displayName?: string;
} = forwardRef(
({ as, children, icon, withArrow, ...props }: LinkProps, ref: React.Ref<HTMLAnchorElement>) => {
({ children, icon, withArrow, ...props }: LinkProps, ref: React.Ref<HTMLAnchorElement>) => {
const LocalIcon = Icon[icon];
return (
<StyledLink as={as} ref={ref} {...props}>
<StyledLink ref={ref} {...props}>
<StyledLeft>
{icon}
{icon && <LocalIcon />}
{children}
</StyledLeft>
{withArrow && <Icon.ChevronRight size={8} />}
Expand All @@ -35,39 +38,35 @@ export const Link: {

Link.displayName = 'Link';

const StyledLink = styled.a<Omit<LinkProps, 'children'>>(({ 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<Omit<LinkProps, 'children'>>(
({ 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',
Expand Down
2 changes: 1 addition & 1 deletion code/ui/manager/src/globals/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit fbbbf14

Please sign in to comment.