Refactor icons passed as props with the new way (#1492)

* Refactor icons passed as props with the new way

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Update more files

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Fix according to review

* Fix according to review

* Fix according to review

* Fix chromatic regressions

---------

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
gitstart-twenty
2023-09-10 19:39:17 +01:00
committed by GitHub
parent 89fed80537
commit fb737e2021
82 changed files with 341 additions and 425 deletions

View File

@ -1,7 +1,8 @@
import React, { useMemo } from 'react';
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { TablerIconsProps } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { SoonPill } from '@/ui/pill/components/SoonPill';
export type ButtonSize = 'medium' | 'small';
@ -11,7 +12,7 @@ export type ButtonAccent = 'default' | 'blue' | 'danger';
export type ButtonProps = {
className?: string;
icon?: React.ReactNode;
Icon?: IconComponent;
title?: string;
fullWidth?: boolean;
variant?: ButtonVariant;
@ -278,7 +279,7 @@ const StyledButton = styled.button<
export function Button({
className,
icon: initialIcon,
Icon,
title,
fullWidth = false,
variant = 'primary',
@ -290,15 +291,7 @@ export function Button({
focus = false,
onClick,
}: ButtonProps) {
const icon = useMemo(() => {
if (!initialIcon || !React.isValidElement(initialIcon)) {
return null;
}
return React.cloneElement<TablerIconsProps>(initialIcon as any, {
size: 14,
});
}, [initialIcon]);
const theme = useTheme();
return (
<StyledButton
@ -312,7 +305,7 @@ export function Button({
className={className}
onClick={onClick}
>
{icon}
{Icon && <Icon size={theme.icon.size.sm} />}
{title}
{soon && <SoonPill />}
</StyledButton>

View File

@ -1,14 +1,15 @@
import React, { useMemo } from 'react';
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { TablerIconsProps } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
export type FloatingButtonSize = 'small' | 'medium';
export type FloatingButtonPosition = 'standalone' | 'left' | 'middle' | 'right';
export type FloatingButtonProps = {
className?: string;
icon?: React.ReactNode;
Icon?: IconComponent;
title?: string;
size?: FloatingButtonSize;
position?: FloatingButtonPosition;
@ -80,7 +81,7 @@ const StyledButton = styled.button<
export function FloatingButton({
className,
icon: initialIcon,
Icon,
title,
size = 'small',
applyBlur = true,
@ -88,16 +89,7 @@ export function FloatingButton({
disabled = false,
focus = false,
}: FloatingButtonProps) {
const icon = useMemo(() => {
if (!initialIcon || !React.isValidElement(initialIcon)) {
return null;
}
return React.cloneElement<TablerIconsProps>(initialIcon as any, {
size: 14,
});
}, [initialIcon]);
const theme = useTheme();
return (
<StyledButton
disabled={disabled}
@ -107,7 +99,7 @@ export function FloatingButton({
applyShadow={applyShadow}
className={className}
>
{icon}
{Icon && <Icon size={theme.icon.size.sm} />}
{title}
</StyledButton>
);

View File

@ -1,7 +1,8 @@
import React, { useMemo } from 'react';
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { TablerIconsProps } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
export type FloatingIconButtonSize = 'small' | 'medium';
export type FloatingIconButtonPosition =
@ -12,7 +13,7 @@ export type FloatingIconButtonPosition =
export type FloatingIconButtonProps = {
className?: string;
icon?: React.ReactNode;
Icon?: IconComponent;
size?: FloatingIconButtonSize;
position?: FloatingIconButtonPosition;
applyShadow?: boolean;
@ -100,7 +101,7 @@ const StyledButton = styled.button<
export function FloatingIconButton({
className,
icon: initialIcon,
Icon,
size = 'small',
position = 'standalone',
applyShadow = true,
@ -109,16 +110,7 @@ export function FloatingIconButton({
focus = false,
onClick,
}: FloatingIconButtonProps) {
const icon = useMemo(() => {
if (!initialIcon || !React.isValidElement(initialIcon)) {
return null;
}
return React.cloneElement<TablerIconsProps>(initialIcon as any, {
size: 16,
});
}, [initialIcon]);
const theme = useTheme();
return (
<StyledButton
disabled={disabled}
@ -130,7 +122,7 @@ export function FloatingIconButton({
position={position}
onClick={onClick}
>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
</StyledButton>
);
}

View File

@ -1,5 +1,4 @@
import type { MouseEvent } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import type { IconComponent } from '@/ui/icon/types/IconComponent';
@ -34,8 +33,6 @@ export function FloatingIconButtonGroup({
iconButtons,
size,
}: FloatingIconButtonGroupProps) {
const theme = useTheme();
return (
<StyledFloatingIconButtonGroupContainer>
{iconButtons.map(({ Icon, onClick }, index) => {
@ -50,7 +47,7 @@ export function FloatingIconButtonGroup({
<FloatingIconButton
applyBlur={false}
applyShadow={false}
icon={<Icon size={theme.icon.size.sm} />}
Icon={Icon}
onClick={onClick}
position={position}
size={size}

View File

@ -1,7 +1,8 @@
import React, { useMemo } from 'react';
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { TablerIconsProps } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
export type IconButtonSize = 'medium' | 'small';
export type IconButtonPosition = 'standalone' | 'left' | 'middle' | 'right';
@ -10,7 +11,7 @@ export type IconButtonAccent = 'default' | 'blue' | 'danger';
export type IconButtonProps = {
className?: string;
icon?: React.ReactNode;
Icon?: IconComponent;
variant?: IconButtonVariant;
size?: IconButtonSize;
position?: IconButtonPosition;
@ -270,7 +271,7 @@ const StyledButton = styled.button<
export function IconButton({
className,
icon: initialIcon,
Icon,
variant = 'primary',
size = 'medium',
accent = 'default',
@ -280,16 +281,7 @@ export function IconButton({
dataTestId,
onClick,
}: IconButtonProps) {
const icon = useMemo(() => {
if (!initialIcon || !React.isValidElement(initialIcon)) {
return <></>;
}
return React.cloneElement<TablerIconsProps>(initialIcon as any, {
size: 16,
});
}, [initialIcon]);
const theme = useTheme();
return (
<StyledButton
data-testid={dataTestId}
@ -302,7 +294,7 @@ export function IconButton({
className={className}
onClick={onClick}
>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
</StyledButton>
);
}

View File

@ -1,11 +1,13 @@
import type { MouseEvent } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import type { IconComponent } from '@/ui/icon/types/IconComponent';
import { Button } from './Button';
import { IconButtonPosition, type IconButtonProps } from './IconButton';
import {
IconButton,
IconButtonPosition,
type IconButtonProps,
} from './IconButton';
const StyledIconButtonGroupContainer = styled.div`
border-radius: ${({ theme }) => theme.border.radius.md};
@ -28,8 +30,6 @@ export function IconButtonGroup({
size,
variant,
}: IconButtonGroupProps) {
const theme = useTheme();
return (
<StyledIconButtonGroupContainer>
{iconButtons.map(({ Icon, onClick }, index) => {
@ -41,9 +41,9 @@ export function IconButtonGroup({
: 'middle';
return (
<Button
<IconButton
accent={accent}
icon={<Icon size={theme.icon.size.sm} />}
Icon={Icon}
onClick={onClick}
position={position}
size={size}

View File

@ -1,14 +1,15 @@
import React, { MouseEvent, useMemo } from 'react';
import React, { MouseEvent } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { TablerIconsProps } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
export type LightIconButtonAccent = 'secondary' | 'tertiary';
export type LightIconButtonSize = 'small' | 'medium';
export type LightIconButtonProps = {
className?: string;
icon?: React.ReactNode;
Icon?: IconComponent;
title?: string;
size?: LightIconButtonSize;
accent?: LightIconButtonAccent;
@ -79,7 +80,7 @@ const StyledButton = styled.button<
export function LightIconButton({
className,
icon: initialIcon,
Icon,
active = false,
size = 'small',
accent = 'secondary',
@ -87,16 +88,7 @@ export function LightIconButton({
focus = false,
onClick,
}: LightIconButtonProps) {
const icon = useMemo(() => {
if (!initialIcon || !React.isValidElement(initialIcon)) {
return null;
}
return React.cloneElement<TablerIconsProps>(initialIcon as any, {
size: 16,
});
}, [initialIcon]);
const theme = useTheme();
return (
<StyledButton
onClick={onClick}
@ -107,7 +99,7 @@ export function LightIconButton({
size={size}
active={active}
>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
</StyledButton>
);
}

View File

@ -1,10 +1,12 @@
import React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/icon/types/IconComponent';
type Variant = 'primary' | 'secondary';
type Props = {
icon?: React.ReactNode;
title: string;
fullWidth?: boolean;
variant?: Variant;
@ -85,16 +87,21 @@ const StyledButton = styled.button<Pick<Props, 'fullWidth' | 'variant'>>`
}};
`;
type MainButtonProps = Props & {
Icon?: IconComponent;
};
export function MainButton({
icon,
Icon,
title,
fullWidth = false,
variant = 'primary',
...props
}: Props) {
}: MainButtonProps) {
const theme = useTheme();
return (
<StyledButton fullWidth={fullWidth} variant={variant} {...props}>
{icon}
{Icon && <Icon size={theme.icon.size.sm} />}
{title}
</StyledButton>
);

View File

@ -1,5 +1,8 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/icon/types/IconComponent';
const StyledIconButton = styled.button`
align-items: center;
background: ${({ theme }) => theme.color.blue};
@ -26,9 +29,16 @@ const StyledIconButton = styled.button`
width: 20px;
`;
export function RoundedIconButton({
icon,
...props
}: { icon: React.ReactNode } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return <StyledIconButton {...props}>{icon}</StyledIconButton>;
type RoundedIconButtonProps = {
Icon: IconComponent;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
export function RoundedIconButton({ Icon, ...props }: RoundedIconButtonProps) {
const theme = useTheme();
return (
<StyledIconButton {...props}>
{<Icon size={theme.icon.size.md} />}
</StyledIconButton>
);
}

View File

@ -31,14 +31,14 @@ export const Default: Story = {
fullWidth: false,
soon: false,
position: 'standalone',
icon: <IconSearch />,
Icon: IconSearch,
className: '',
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { title: 'Filter', icon: <IconSearch /> },
args: { title: 'Filter', Icon: IconSearch },
argTypes: {
size: { control: false },
variant: { control: false },
@ -110,7 +110,7 @@ export const Catalog: Story = {
};
export const SoonCatalog: Story = {
args: { title: 'Filter', icon: <IconSearch />, soon: true },
args: { title: 'Filter', Icon: IconSearch, soon: true },
argTypes: {
size: { control: false },
variant: { control: false },
@ -182,7 +182,7 @@ export const SoonCatalog: Story = {
};
export const PositionCatalog: Story = {
args: { title: 'Filter', icon: <IconSearch /> },
args: { title: 'Filter', Icon: IconSearch },
argTypes: {
size: { control: false },
variant: { control: false },
@ -258,7 +258,7 @@ export const PositionCatalog: Story = {
};
export const FullWidth: Story = {
args: { title: 'Filter', icon: <IconSearch />, fullWidth: true },
args: { title: 'Filter', Icon: IconSearch, fullWidth: true },
argTypes: {
size: { control: false },
variant: { control: false },
@ -269,7 +269,7 @@ export const FullWidth: Story = {
soon: { control: false },
position: { control: false },
className: { control: false },
icon: { control: false },
Icon: { control: false },
},
decorators: [ComponentDecorator],
};

View File

@ -21,9 +21,9 @@ export const Default: Story = {
variant: 'primary',
accent: 'danger',
children: [
<Button icon={<IconNotes />} title="Note" />,
<Button icon={<IconCheckbox />} title="Task" />,
<Button icon={<IconTimelineEvent />} title="Activity" />,
<Button Icon={IconNotes} title="Note" />,
<Button Icon={IconCheckbox} title="Task" />,
<Button Icon={IconTimelineEvent} title="Activity" />,
],
},
argTypes: {
@ -35,9 +35,9 @@ export const Default: Story = {
export const Catalog: Story = {
args: {
children: [
<Button icon={<IconNotes />} title="Note" />,
<Button icon={<IconCheckbox />} title="Task" />,
<Button icon={<IconTimelineEvent />} title="Activity" />,
<Button Icon={IconNotes} title="Note" />,
<Button Icon={IconCheckbox} title="Task" />,
<Button Icon={IconTimelineEvent} title="Activity" />,
],
},
argTypes: {

View File

@ -23,16 +23,16 @@ export const Default: Story = {
applyBlur: true,
applyShadow: true,
position: 'standalone',
icon: <IconSearch />,
Icon: IconSearch,
},
argTypes: {
icon: { control: false },
Icon: { control: false },
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { title: 'Filter', icon: <IconSearch /> },
args: { title: 'Filter', Icon: IconSearch },
argTypes: {
size: { control: false },
disabled: { control: false },

View File

@ -19,9 +19,9 @@ export const Default: Story = {
args: {
size: 'small',
children: [
<FloatingButton icon={<IconNotes />} />,
<FloatingButton icon={<IconCheckbox />} />,
<FloatingButton icon={<IconTimelineEvent />} />,
<FloatingButton Icon={IconNotes} />,
<FloatingButton Icon={IconCheckbox} />,
<FloatingButton Icon={IconTimelineEvent} />,
],
},
argTypes: {
@ -33,9 +33,9 @@ export const Default: Story = {
export const Catalog: Story = {
args: {
children: [
<FloatingButton icon={<IconNotes />} />,
<FloatingButton icon={<IconCheckbox />} />,
<FloatingButton icon={<IconTimelineEvent />} />,
<FloatingButton Icon={IconNotes} />,
<FloatingButton Icon={IconCheckbox} />,
<FloatingButton Icon={IconTimelineEvent} />,
],
},
argTypes: {

View File

@ -25,16 +25,16 @@ export const Default: Story = {
applyBlur: true,
applyShadow: true,
position: 'standalone',
icon: <IconSearch />,
Icon: IconSearch,
},
argTypes: {
icon: { control: false },
Icon: { control: false },
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { icon: <IconSearch /> },
args: { Icon: IconSearch },
argTypes: {
size: { control: false },
disabled: { control: false },

View File

@ -28,20 +28,20 @@ export const Default: Story = {
disabled: false,
focus: false,
position: 'standalone',
icon: <IconSearch />,
Icon: IconSearch,
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { icon: <IconSearch /> },
args: { Icon: IconSearch },
argTypes: {
size: { control: false },
variant: { control: false },
focus: { control: false },
accent: { control: false },
disabled: { control: false },
icon: { control: false },
Icon: { control: false },
position: { control: false },
},
parameters: {
@ -104,7 +104,7 @@ export const Catalog: Story = {
};
export const PositionCatalog: Story = {
args: { icon: <IconSearch /> },
args: { Icon: IconSearch },
argTypes: {
size: { control: false },
variant: { control: false },
@ -112,7 +112,7 @@ export const PositionCatalog: Story = {
accent: { control: false },
disabled: { control: false },
position: { control: false },
icon: { control: false },
Icon: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.pressed'] },

View File

@ -25,16 +25,16 @@ export const Default: Story = {
disabled: false,
active: false,
focus: false,
icon: <IconSearch />,
Icon: IconSearch,
},
argTypes: {
icon: { control: false },
Icon: { control: false },
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { title: 'Filter', icon: <IconSearch /> },
args: { title: 'Filter', Icon: IconSearch },
argTypes: {
accent: { control: false },
disabled: { control: false },

View File

@ -13,15 +13,6 @@ const meta: Meta<typeof MainButton> = {
title: 'UI/Button/MainButton',
component: MainButton,
decorators: [ComponentDecorator],
argTypes: {
icon: {
type: 'boolean',
mapping: {
true: <IconBrandGoogle size={16} stroke={4} />,
false: undefined,
},
},
},
args: { title: 'A primary Button', onClick: clickJestFn },
};
@ -41,7 +32,7 @@ export const Default: Story = {
};
export const WithIcon: Story = {
args: { icon: true },
args: { Icon: IconBrandGoogle },
};
export const DisabledWithIcon: Story = {

View File

@ -19,8 +19,8 @@ type Story = StoryObj<typeof RoundedIconButton>;
export const Default: Story = {
decorators: [ComponentDecorator],
argTypes: { icon: { control: false } },
args: { onClick: clickJestFn, icon: <IconArrowRight size={15} /> },
argTypes: { Icon: { control: false } },
args: { onClick: clickJestFn, Icon: IconArrowRight },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);