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,8 +1,10 @@
import { ReactNode } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/icon/types/IconComponent';
type OwnProps = {
icon: ReactNode;
Icon: IconComponent;
label: string;
type?: 'standard' | 'danger';
onClick: () => void;
@ -39,13 +41,14 @@ const StyledButtonLabel = styled.div`
export function ActionBarEntry({
label,
icon,
Icon,
type = 'standard',
onClick,
}: OwnProps) {
const theme = useTheme();
return (
<StyledButton type={type} onClick={onClick}>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
<StyledButtonLabel>{label}</StyledButtonLabel>
</StyledButton>
);

View File

@ -16,7 +16,7 @@ export function useBoardActionBarEntries() {
setActionBarEntries([
<ActionBarEntry
label="Delete"
icon={<IconTrash size={16} />}
Icon={IconTrash}
type="danger"
onClick={deleteSelectedBoardCards}
key="delete"

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);

View File

@ -14,7 +14,7 @@ export function EditableFieldEditButton() {
<FloatingIconButton
size="small"
onClick={handleClick}
icon={<IconPencil />}
Icon={IconPencil}
data-testid="editable-field-edit-mode-container"
/>
);

View File

@ -198,7 +198,7 @@ export function AutosizeTextInput({
<StyledBottomRightRoundedIconButton>
<RoundedIconButton
onClick={handleOnClickSendButton}
icon={<IconArrowRight size={15} />}
Icon={IconArrowRight}
disabled={isSendButtonDisabled}
/>
</StyledBottomRightRoundedIconButton>

View File

@ -135,7 +135,7 @@ export function ImageInput({
/>
{isUploading && onAbort ? (
<Button
icon={<IconX />}
Icon={IconX}
onClick={onAbort}
variant="secondary"
title="Abort"
@ -144,7 +144,7 @@ export function ImageInput({
/>
) : (
<Button
icon={<IconUpload />}
Icon={IconUpload}
onClick={onUploadButtonClick}
variant="secondary"
title="Upload"
@ -153,7 +153,7 @@ export function ImageInput({
/>
)}
<Button
icon={<IconTrash />}
Icon={IconTrash}
onClick={onRemove}
variant="secondary"
title="Remove"

View File

@ -8,7 +8,7 @@ type OwnProps = {
export function PageAddButton({ onClick }: OwnProps) {
return (
<IconButton
icon={<IconPlus size={16} />}
Icon={IconPlus}
size="medium"
variant="secondary"
data-testid="add-button"

View File

@ -9,7 +9,7 @@ type OwnProps = {
export function PageFavoriteButton({ isFavorite, onClick }: OwnProps) {
return (
<IconButton
icon={<IconHeart size={16} />}
Icon={IconHeart}
size="medium"
variant="secondary"
data-testid="add-button"

View File

@ -1,12 +1,13 @@
import { type ComponentProps, type ReactNode, useCallback } from 'react';
import { type ComponentProps, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconButton } from '@/ui/button/components/IconButton';
import { IconButton, IconButtonSize } from '@/ui/button/components/IconButton';
import { IconChevronLeft } from '@/ui/icon/index';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton';
import { navbarIconSize } from '@/ui/navbar/constants';
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
@ -63,28 +64,27 @@ const StyledPageActionContainer = styled.div`
gap: ${({ theme }) => theme.spacing(2)};
`;
type OwnProps = ComponentProps<'div'> & {
type PageHeaderProps = ComponentProps<'div'> & {
title: string;
hasBackButton?: boolean;
icon: ReactNode;
Icon: IconComponent;
children?: JSX.Element | JSX.Element[];
};
export function PageHeader({
title,
hasBackButton,
icon,
Icon,
children,
...props
}: OwnProps) {
}: PageHeaderProps) {
const navigate = useNavigate();
const navigateBack = useCallback(() => navigate(-1), [navigate]);
const isNavbarOpened = useRecoilValue(isNavbarOpenedState);
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
const iconSize: IconButtonSize = useIsMobile() ? 'small' : 'medium';
const theme = useTheme();
return (
<StyledTopBarContainer {...props}>
@ -97,14 +97,15 @@ export function PageHeader({
{hasBackButton && (
<StyledTopBarButtonContainer>
<StyledBackIconButton
icon={<IconChevronLeft size={iconSize} />}
Icon={IconChevronLeft}
size={iconSize}
onClick={navigateBack}
variant="tertiary"
/>
</StyledTopBarButtonContainer>
)}
<StyledTopBarIconStyledTitleContainer>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
<StyledTitleContainer data-testid="top-bar-title">
<OverflowingTextWithTooltip text={title} />
</StyledTitleContainer>

View File

@ -1,14 +1,16 @@
import { JSX } from 'react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { PageHeader } from './PageHeader';
import { RightDrawerContainer } from './RightDrawerContainer';
type OwnProps = {
type SubMenuTopBarContainerProps = {
children: JSX.Element | JSX.Element[];
title: string;
icon: React.ReactNode;
Icon: IconComponent;
};
const StyledContainer = styled.div<{ isMobile: boolean }>`
@ -18,12 +20,16 @@ const StyledContainer = styled.div<{ isMobile: boolean }>`
width: 100%;
`;
export function SubMenuTopBarContainer({ children, title, icon }: OwnProps) {
export function SubMenuTopBarContainer({
children,
title,
Icon,
}: SubMenuTopBarContainerProps) {
const isMobile = useIsMobile();
return (
<StyledContainer isMobile={isMobile}>
{isMobile && <PageHeader title={title} icon={icon} />}
{isMobile && <PageHeader title={title} Icon={Icon} />}
<RightDrawerContainer topMargin={16}>{children}</RightDrawerContainer>
</StyledContainer>
);

View File

@ -37,7 +37,7 @@ export function ShowPageAddButton({
dropdownKey="add-show-page"
buttonComponents={
<IconButton
icon={<IconPlus size={16} />}
Icon={IconPlus}
size="medium"
dataTestId="add-showpage-button"
accent="default"

View File

@ -1,4 +1,3 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Notes } from '@/activities/notes/components/Notes';
@ -53,31 +52,29 @@ export function ShowPageRightContainer({
notes,
emails,
}: OwnProps) {
const theme = useTheme();
const TASK_TABS = [
{
id: 'timeline',
title: 'Timeline',
icon: <IconTimelineEvent size={theme.icon.size.md} />,
Icon: IconTimelineEvent,
hide: !timeline,
},
{
id: 'tasks',
title: 'Tasks',
icon: <IconCheckbox size={theme.icon.size.md} />,
Icon: IconCheckbox,
hide: !tasks,
},
{
id: 'notes',
title: 'Notes',
icon: <IconNotes size={theme.icon.size.md} />,
Icon: IconNotes,
hide: !notes,
},
{
id: 'emails',
title: 'Emails',
icon: <IconMail size={theme.icon.size.md} />,
Icon: IconMail,
hide: !emails,
disabled: true,
},

View File

@ -40,7 +40,7 @@ export type MenuItemProps = {
LeftIcon?: IconComponent;
text: string;
command: string;
className: string;
className?: string;
onClick?: () => void;
};

View File

@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { IconButton } from '@/ui/button/components/IconButton';
import {
IconLayoutSidebarLeftCollapse,
IconLayoutSidebarRightCollapse,
@ -8,8 +9,6 @@ import {
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { navbarIconSize } from '../constants';
const StyledCollapseButton = styled.button<{
hide: boolean;
}>`
@ -57,9 +56,7 @@ export default function NavCollapseButton({
const [isNavbarOpened, setIsNavbarOpened] =
useRecoilState(isNavbarOpenedState);
const iconSize = useIsMobile()
? navbarIconSize.mobile
: navbarIconSize.desktop;
const iconSize = useIsMobile() ? 'small' : 'medium';
return (
<>
@ -68,14 +65,22 @@ export default function NavCollapseButton({
hide={hide}
onClick={() => setIsNavbarOpened(!isNavbarOpened)}
>
<IconLayoutSidebarLeftCollapse size={iconSize} />
<IconButton
Icon={IconLayoutSidebarLeftCollapse}
variant="tertiary"
size={iconSize}
/>
</StyledCollapseButton>
) : (
<StyledCollapseButton
hide={hide}
onClick={() => setIsNavbarOpened(!isNavbarOpened)}
>
<IconLayoutSidebarRightCollapse size={iconSize} />
<IconButton
Icon={IconLayoutSidebarRightCollapse}
variant="tertiary"
size={iconSize}
/>
</StyledCollapseButton>
)}
</>

View File

@ -1,19 +1,20 @@
import { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { isNavbarOpenedState } from '../../layout/states/isNavbarOpenedState';
type OwnProps = {
type NavItemProps = {
label: string;
to?: string;
onClick?: () => void;
Icon: IconComponent;
active?: boolean;
icon: ReactNode;
danger?: boolean;
soon?: boolean;
};
@ -81,7 +82,16 @@ const StyledSoonPill = styled.div`
padding-right: ${({ theme }) => theme.spacing(2)};
`;
function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
function NavItem({
label,
Icon,
to,
onClick,
active,
danger,
soon,
}: NavItemProps) {
const theme = useTheme();
const navigate = useNavigate();
const [, setIsNavbarOpened] = useRecoilState(isNavbarOpenedState);
@ -107,7 +117,7 @@ function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
danger={danger}
soon={soon}
>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
<StyledItemLabel>{label}</StyledItemLabel>
{soon && <StyledSoonPill>Soon</StyledSoonPill>}
</StyledItem>

View File

@ -1,5 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
@ -30,7 +29,6 @@ function insertScript({
}
export default function SupportChat() {
const theme = useTheme();
const currentUser = useRecoilValue(currentUserState);
const supportChat = useRecoilValue(supportChatState);
const [isFrontChatLoaded, setIsFrontChatLoaded] = useState(false);
@ -85,7 +83,7 @@ export default function SupportChat() {
variant={'tertiary'}
size={'small'}
title="Support"
icon={<IconHelpCircle size={theme.icon.size.md} />}
Icon={IconHelpCircle}
onClick={() => window.FrontChat?.('show')}
/>
</StyledButtonContainer>

View File

@ -8,9 +8,4 @@ export const leftSubMenuNavbarWidth = {
desktop: '520px',
};
export const navbarIconSize = {
mobile: 18,
desktop: 16,
};
export const githubLink = 'https://github.com/twentyhq/twenty';

View File

@ -12,7 +12,7 @@ export function RightDrawerTopBarCloseButton() {
return (
<LightIconButton
icon={<IconChevronsRight />}
Icon={IconChevronsRight}
onClick={handleButtonClick}
size="medium"
accent="tertiary"

View File

@ -21,12 +21,10 @@ export function RightDrawerTopBarExpandButton() {
<LightIconButton
size="medium"
accent="tertiary"
icon={
isRightDrawerExpanded ? (
<IconLayoutSidebarRightCollapse />
) : (
<IconLayoutSidebarRightExpand />
)
Icon={
isRightDrawerExpanded
? IconLayoutSidebarRightCollapse
: IconLayoutSidebarRightExpand
}
onClick={handleButtonClick}
/>

View File

@ -1,10 +1,13 @@
import * as React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
type OwnProps = {
import { IconComponent } from '@/ui/icon/types/IconComponent';
type TabProps = {
id: string;
title: string;
icon?: React.ReactNode;
Icon?: IconComponent;
active?: boolean;
className?: string;
onClick?: () => void;
@ -47,12 +50,13 @@ const StyledHover = styled.span`
export function Tab({
id,
title,
icon,
Icon,
active = false,
onClick,
className,
disabled,
}: OwnProps) {
}: TabProps) {
const theme = useTheme();
return (
<StyledTab
onClick={onClick}
@ -62,7 +66,7 @@ export function Tab({
data-testid={'tab-' + id}
>
<StyledHover>
{icon}
{Icon && <Icon size={theme.icon.size.md} />}
{title}
</StyledHover>
</StyledTab>

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { activeTabIdScopedState } from '../states/activeTabIdScopedState';
@ -8,18 +9,18 @@ import { Tab } from './Tab';
type SingleTabProps = {
title: string;
icon?: React.ReactNode;
Icon?: IconComponent;
id: string;
hide?: boolean;
disabled?: boolean;
};
type OwnProps = {
type TabListProps = {
tabs: SingleTabProps[];
context: React.Context<string | null>;
};
export function TabList({ tabs, context }: OwnProps) {
export function TabList({ tabs, context }: TabListProps) {
const initialActiveTabId = tabs[0].id;
const [activeTabId, setActiveTabId] = useRecoilScopedState(
@ -40,7 +41,7 @@ export function TabList({ tabs, context }: OwnProps) {
id={tab.id}
key={tab.id}
title={tab.title}
icon={tab.icon}
Icon={tab.Icon}
active={tab.id === activeTabId}
onClick={() => {
setActiveTabId(tab.id);

View File

@ -23,7 +23,7 @@ export const Default: Story = {
};
export const Catalog: Story = {
args: { title: 'Tab title', icon: <IconCheckbox /> },
args: { title: 'Tab title', Icon: IconCheckbox },
argTypes: {
active: { control: false },
disabled: { control: false },

View File

@ -12,26 +12,26 @@ const tabs = [
{
id: '1',
title: 'Tab1',
icon: <IconCheckbox size={16} />,
Icon: IconCheckbox,
hide: true,
},
{
id: '2',
title: 'Tab2',
icon: <IconCheckbox size={16} />,
Icon: IconCheckbox,
hide: false,
},
{
id: '3',
title: 'Tab3',
icon: <IconCheckbox size={16} />,
Icon: IconCheckbox,
hide: false,
disabled: true,
},
{
id: '4',
title: 'Tab4',
icon: <IconCheckbox size={16} />,
Icon: IconCheckbox,
hide: false,
disabled: false,
},

View File

@ -189,7 +189,7 @@ export function EntityTableHeader() {
<IconButton
size="medium"
variant="tertiary"
icon={<IconPlus />}
Icon={IconPlus}
onClick={toggleColumnMenu}
position="middle"
/>

View File

@ -117,7 +117,7 @@ export function EditableCell({
<FloatingIconButton
size="small"
onClick={handlePenClick}
icon={<IconPencil size={14} />}
Icon={IconPencil}
/>
</StyledEditButtonContainer>
)}

View File

@ -1,18 +1,20 @@
import { ReactNode } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Key } from 'ts-key-enum';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { DropdownMenuContainer } from './DropdownMenuContainer';
type OwnProps = {
type DropdownButtonProps = {
anchor?: 'left' | 'right';
label: ReactNode;
isActive: boolean;
children?: ReactNode;
isUnfolded?: boolean;
icon?: ReactNode;
Icon?: IconComponent;
onIsUnfoldedChange?: (newIsUnfolded: boolean) => void;
resetState?: () => void;
hotkeyScope: string;
@ -64,11 +66,11 @@ function DropdownButton({
children,
isUnfolded = false,
onIsUnfoldedChange,
Icon,
hotkeyScope,
icon,
color,
menuWidth,
}: OwnProps) {
}: DropdownButtonProps) {
useScopedHotkeys(
[Key.Enter, Key.Escape],
() => {
@ -86,6 +88,8 @@ function DropdownButton({
onIsUnfoldedChange?.(false);
};
const theme = useTheme();
return (
<StyledDropdownButtonContainer>
<StyledDropdownButton
@ -95,7 +99,11 @@ function DropdownButton({
aria-selected={isActive}
color={color}
>
{icon && <StyledDropdownButtonIcon>{icon}</StyledDropdownButtonIcon>}
{Icon && (
<StyledDropdownButtonIcon>
{<Icon size={theme.icon.size.md} />}
</StyledDropdownButtonIcon>
)}
{label}
</StyledDropdownButton>
{isUnfolded && (

View File

@ -1,5 +1,6 @@
import { Context } from 'react';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
@ -8,21 +9,23 @@ import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope';
import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton';
import { SingleEntityFilterDropdownButton } from './SingleEntityFilterDropdownButton';
type FilterDropdownButtonProps = {
context: Context<string | null>;
hotkeyScope: FiltersHotkeyScope;
isPrimaryButton?: boolean;
Icon?: IconComponent;
color?: string;
label?: string;
};
export function FilterDropdownButton({
context,
hotkeyScope,
isPrimaryButton = false,
color,
icon,
Icon,
label,
}: {
context: Context<string | null>;
hotkeyScope: FiltersHotkeyScope;
isPrimaryButton?: boolean;
icon?: React.ReactNode;
color?: string;
label?: string;
}) {
}: FilterDropdownButtonProps) {
const [availableFilters] = useRecoilScopedState(
availableFiltersScopedState,
context,
@ -40,7 +43,7 @@ export function FilterDropdownButton({
<MultipleFiltersDropdownButton
context={context}
hotkeyScope={hotkeyScope}
icon={icon}
Icon={Icon}
isPrimaryButton={isPrimaryButton}
color={color}
label={label}

View File

@ -1,6 +1,7 @@
import { Context, useCallback, useState } from 'react';
import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/view-bar/states/filterDefinitionUsedInDropdownScopedState';
@ -22,21 +23,23 @@ import { FilterDropdownOperandButton } from './FilterDropdownOperandButton';
import { FilterDropdownOperandSelect } from './FilterDropdownOperandSelect';
import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput';
type MultipleFiltersDropdownButtonProps = {
context: Context<string | null>;
hotkeyScope: FiltersHotkeyScope;
isPrimaryButton?: boolean;
Icon?: IconComponent;
color?: string;
label?: string;
};
export function MultipleFiltersDropdownButton({
context,
hotkeyScope,
isPrimaryButton = false,
color,
icon,
Icon,
label,
}: {
context: Context<string | null>;
hotkeyScope: FiltersHotkeyScope;
isPrimaryButton?: boolean;
icon?: React.ReactNode;
color?: string;
label?: string;
}) {
}: MultipleFiltersDropdownButtonProps) {
const [isUnfolded, setIsUnfolded] = useState(false);
const [
@ -108,7 +111,7 @@ export function MultipleFiltersDropdownButton({
label={label ?? 'Filter'}
isActive={isFilterSelected}
isUnfolded={isUnfolded}
icon={icon}
Icon={Icon}
onIsUnfoldedChange={handleIsUnfoldedChange}
hotkeyScope={hotkeyScope}
color={color}

View File

@ -67,7 +67,7 @@ function SortOrFilterChip({
<StyledChip isSort={isSort}>
{Icon && (
<StyledIcon>
<Icon />
<Icon size={theme.icon.size.md} />
</StyledIcon>
)}
{labelKey && <StyledLabelKey>{labelKey}</StyledLabelKey>}

View File

@ -108,7 +108,7 @@ export const UpdateViewButtonGroup = ({
/>
<Button
size="small"
icon={<IconChevronDown />}
Icon={IconChevronDown}
onClick={handleArrowDownButtonClick}
/>
</ButtonGroup>

View File

@ -202,7 +202,7 @@ function ViewBarDetails<SortField>({
context={context}
hotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
color={theme.font.color.tertiary}
icon={<IconPlus size={theme.icon.size.md} />}
Icon={IconPlus}
label="Add filter"
/>
</StyledAddFilterContainer>