Migrate to a monorepo structure (#2909)

This commit is contained in:
Charles Bochet
2023-12-10 18:10:54 +01:00
committed by GitHub
parent a70a9281eb
commit 5bdca9de6c
2304 changed files with 37152 additions and 25869 deletions

View File

@ -0,0 +1,78 @@
import * as React from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
type TabProps = {
id: string;
title: string;
Icon?: IconComponent;
active?: boolean;
className?: string;
onClick?: () => void;
disabled?: boolean;
};
const StyledTab = styled.div<{ active?: boolean; disabled?: boolean }>`
align-items: center;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-color: ${({ theme, active }) =>
active ? theme.border.color.inverted : 'transparent'};
color: ${({ theme, active, disabled }) =>
active
? theme.font.color.primary
: disabled
? theme.font.color.light
: theme.font.color.secondary};
cursor: pointer;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
justify-content: center;
margin-bottom: -1px;
padding: ${({ theme }) => theme.spacing(2) + ' 0'};
pointer-events: ${({ disabled }) => (disabled ? 'none' : '')};
`;
const StyledHover = styled.span`
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
&:hover {
background: ${({ theme }) => theme.background.tertiary};
border-radius: ${({ theme }) => theme.border.radius.sm};
}
&:active {
background: ${({ theme }) => theme.background.quaternary};
}
`;
export const Tab = ({
id,
title,
Icon,
active = false,
onClick,
className,
disabled,
}: TabProps) => {
const theme = useTheme();
return (
<StyledTab
onClick={onClick}
active={active}
className={className}
disabled={disabled}
data-testid={'tab-' + id}
>
<StyledHover>
{Icon && <Icon size={theme.icon.size.md} />}
{title}
</StyledHover>
</StyledTab>
);
};

View File

@ -0,0 +1,65 @@
import * as React from 'react';
import styled from '@emotion/styled';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { activeTabIdScopedState } from '../states/activeTabIdScopedState';
import { Tab } from './Tab';
type SingleTabProps = {
title: string;
Icon?: IconComponent;
id: string;
hide?: boolean;
disabled?: boolean;
};
type TabListProps = {
tabs: SingleTabProps[];
context: React.Context<string | null>;
};
const StyledContainer = styled.div`
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: 40px;
padding-left: ${({ theme }) => theme.spacing(2)};
user-select: none;
`;
export const TabList = ({ tabs, context }: TabListProps) => {
const initialActiveTabId = tabs[0].id;
const [activeTabId, setActiveTabId] = useRecoilScopedState(
activeTabIdScopedState,
context,
);
React.useEffect(() => {
setActiveTabId(initialActiveTabId);
}, [initialActiveTabId, setActiveTabId]);
return (
<StyledContainer>
{tabs
.filter((tab) => !tab.hide)
.map((tab) => (
<Tab
id={tab.id}
key={tab.id}
title={tab.title}
Icon={tab.Icon}
active={tab.id === activeTabId}
onClick={() => {
setActiveTabId(tab.id);
}}
disabled={tab.disabled}
/>
))}
</StyledContainer>
);
};

View File

@ -0,0 +1,64 @@
import { Meta, StoryObj } from '@storybook/react';
import { IconCheckbox } from '@/ui/display/icon';
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { CatalogStory } from '~/testing/types';
import { Tab } from '../Tab';
const meta: Meta<typeof Tab> = {
title: 'UI/Layout/Tab/Tab',
component: Tab,
};
export default meta;
type Story = StoryObj<typeof Tab>;
export const Default: Story = {
args: {
title: 'Tab title',
active: false,
Icon: IconCheckbox,
disabled: false,
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof Tab> = {
args: { title: 'Tab title', Icon: IconCheckbox },
argTypes: {
active: { control: false },
disabled: { control: false },
onClick: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'], active: ['.active'] },
catalog: {
dimensions: [
{
name: 'states',
values: ['default', 'hover', 'active'],
props: (state: string) =>
state === 'default' ? {} : { className: state },
},
{
name: 'Active',
values: ['true', 'false'],
labels: (active: string) =>
active === 'true' ? 'active' : 'inactive',
props: (active: string) => ({ active: active === 'true' }),
},
{
name: 'Disabled',
values: ['true', 'false'],
labels: (disabled: string) =>
disabled === 'true' ? 'disabled' : 'enabled',
props: (disabled: string) => ({ disabled: disabled === 'true' }),
},
],
},
},
decorators: [CatalogDecorator],
};

View File

@ -0,0 +1,68 @@
import { Meta, StoryObj } from '@storybook/react';
import { expect, within } from '@storybook/test';
import { IconCheckbox } from '@/ui/display/icon';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { TabList } from '../TabList';
const tabs = [
{
id: '1',
title: 'Tab1',
Icon: IconCheckbox,
hide: true,
},
{
id: '2',
title: 'Tab2',
Icon: IconCheckbox,
hide: false,
},
{
id: '3',
title: 'Tab3',
Icon: IconCheckbox,
hide: false,
disabled: true,
},
{
id: '4',
title: 'Tab4',
Icon: IconCheckbox,
hide: false,
disabled: false,
},
];
const meta: Meta<typeof TabList> = {
title: 'UI/Layout/Tab/TabList',
component: TabList,
args: {
tabs: tabs,
},
decorators: [
(Story) => (
<RecoilScope>
<Story />
</RecoilScope>
),
ComponentDecorator,
],
};
export default meta;
type Story = StoryObj<typeof TabList>;
export const TabListDisplay: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const submitButton = canvas.queryByText('Tab1');
expect(submitButton).toBeNull();
expect(await canvas.findByText('Tab2')).toBeInTheDocument();
expect(await canvas.findByText('Tab3')).toBeInTheDocument();
expect(await canvas.findByText('Tab4')).toBeInTheDocument();
},
};