[UI] Extract our ColorSample and Tag components from twenty-front to twenty-ui. (#5543)

Two more components extracted out of twenty-front: `ColorSample` and
`Tag`.
This commit is contained in:
Abdullah
2024-05-23 10:46:31 +05:00
committed by GitHub
parent 6b1d4e0744
commit b8eef21343
21 changed files with 66 additions and 65 deletions

View File

@ -0,0 +1,41 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { ThemeColor } from '@ui/theme';
export type ColorSampleVariant = 'default' | 'pipeline';
export type ColorSampleProps = {
colorName: ThemeColor;
variant?: ColorSampleVariant;
};
const StyledColorSample = styled.div<ColorSampleProps>`
background-color: ${({ theme, colorName }) =>
theme.tag.background[colorName]};
border: 1px solid ${({ theme, colorName }) => theme.tag.text[colorName]};
border-radius: 60px;
height: ${({ theme }) => theme.spacing(4)};
width: ${({ theme }) => theme.spacing(3)};
${({ colorName, theme, variant }) => {
if (variant === 'pipeline')
return css`
align-items: center;
border: 0;
display: flex;
justify-content: center;
&:after {
background-color: ${theme.tag.text[colorName]};
border-radius: ${theme.border.radius.rounded};
content: '';
display: block;
height: ${theme.spacing(1)};
width: ${theme.spacing(1)};
}
`;
}}
`;
export { StyledColorSample as ColorSample };

View File

@ -0,0 +1,25 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '@ui/testing';
import { ColorSample } from '../ColorSample';
const meta: Meta<typeof ColorSample> = {
title: 'UI/Display/Color/ColorSample',
component: ColorSample,
decorators: [ComponentDecorator],
args: { colorName: 'green' },
argTypes: {
as: { control: false },
theme: { control: false },
},
};
export default meta;
type Story = StoryObj<typeof ColorSample>;
export const Default: Story = {};
export const Pipeline: Story = {
args: { variant: 'pipeline' },
};

View File

@ -4,6 +4,7 @@ export * from './checkmark/components/AnimatedCheckmark';
export * from './checkmark/components/Checkmark';
export * from './chip/components/Chip';
export * from './chip/components/EntityChip';
export * from './color/components/ColorSample';
export * from './icon/components/IconAddressBook';
export * from './icon/components/IconGmail';
export * from './icon/components/IconGoogle';
@ -16,6 +17,7 @@ export * from './icon/hooks/useIcons';
export * from './icon/providers/IconsProvider';
export * from './icon/states/iconsState';
export * from './icon/types/IconComponent';
export * from './tag/components/Tag';
export * from './tooltip/AppTooltip';
export * from './tooltip/OverflowingTextWithTooltip';
export * from './typography/components/H1Title';

View File

@ -0,0 +1,76 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconComponent, OverflowingTextWithTooltip } from '@ui/display';
import { ThemeColor, themeColorSchema } from '@ui/theme';
const StyledTag = styled.h3<{
color: ThemeColor;
weight: TagWeight;
}>`
align-items: center;
background: ${({ color, theme }) => theme.tag.background[color]};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ color, theme }) => theme.tag.text[color]};
display: inline-flex;
font-size: ${({ theme }) => theme.font.size.md};
font-style: normal;
font-weight: ${({ theme, weight }) =>
weight === 'regular'
? theme.font.weight.regular
: theme.font.weight.medium};
height: ${({ theme }) => theme.spacing(5)};
margin: 0;
overflow: hidden;
padding: 0 ${({ theme }) => theme.spacing(2)};
`;
const StyledContent = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledIconContainer = styled.div`
display: flex;
margin-right: ${({ theme }) => theme.spacing(1)};
`;
type TagWeight = 'regular' | 'medium';
type TagProps = {
className?: string;
color: ThemeColor;
text: string;
Icon?: IconComponent;
onClick?: () => void;
weight?: TagWeight;
};
export const Tag = ({
className,
color,
text,
Icon,
onClick,
weight = 'regular',
}: TagProps) => {
const theme = useTheme();
return (
<StyledTag
className={className}
color={themeColorSchema.catch('gray').parse(color)}
onClick={onClick}
weight={weight}
>
{!!Icon && (
<StyledIconContainer>
<Icon size={theme.icon.size.sm} stroke={theme.icon.stroke.sm} />
</StyledIconContainer>
)}
<StyledContent>
<OverflowingTextWithTooltip text={text} />
</StyledContent>
</StyledTag>
);
};

View File

@ -0,0 +1,67 @@
import { Meta, StoryObj } from '@storybook/react';
import { expect, fn, userEvent, within } from '@storybook/test';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
} from '@ui/testing';
import { MAIN_COLOR_NAMES, ThemeColor } from '@ui/theme';
import { Tag } from '../Tag';
const meta: Meta<typeof Tag> = {
title: 'UI/Display/Tag/Tag',
component: Tag,
args: {
text: 'Urgent',
},
};
export default meta;
type Story = StoryObj<typeof Tag>;
export const Default: Story = {
args: {
color: 'red',
onClick: fn(),
},
decorators: [ComponentDecorator],
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const tag = canvas.getByRole('heading', { level: 3 });
await userEvent.click(tag);
await expect(args.onClick).toHaveBeenCalled();
},
};
export const WithLongText: Story = {
decorators: [ComponentDecorator],
args: {
color: 'green',
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
parameters: {
container: { width: 100 },
},
};
export const Catalog: CatalogStory<Story, typeof Tag> = {
argTypes: {
color: { control: false },
},
parameters: {
catalog: {
dimensions: [
{
name: 'colors',
values: MAIN_COLOR_NAMES,
props: (color: ThemeColor) => ({ color }),
},
],
},
},
decorators: [CatalogDecorator],
};

View File

@ -33,3 +33,4 @@ export * from './constants/ThemeLight';
export * from './provider/ThemeProvider';
export * from './types/ThemeType';
export * from './utils/getNextThemeColor';
export * from './utils/themeColorSchema';

View File

@ -0,0 +1,7 @@
import { z } from 'zod';
import { MAIN_COLOR_NAMES, ThemeColor } from '@ui/theme';
export const themeColorSchema = z.enum(
MAIN_COLOR_NAMES as [ThemeColor, ...ThemeColor[]],
);