@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
const StyledSoonPill = styled.span`
|
const StyledSoonPill = styled.span`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ theme }) => theme.background.transparent.light};
|
background: ${({ theme }) => theme.background.transparent.light};
|
||||||
border-radius: 50px;
|
border-radius: ${({ theme }) => theme.border.radius.pill};
|
||||||
color: ${({ theme }) => theme.font.color.light};
|
color: ${({ theme }) => theme.font.color.light};
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
font-size: ${({ theme }) => theme.font.size.xs};
|
||||||
|
|||||||
55
front/src/modules/ui/display/status/components/Status.tsx
Normal file
55
front/src/modules/ui/display/status/components/Status.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||||
|
|
||||||
|
const StyledStatus = styled.h3<{
|
||||||
|
color: ThemeColor;
|
||||||
|
}>`
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ color, theme }) => theme.tag.background[color]};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.pill};
|
||||||
|
color: ${({ color, theme }) => theme.tag.text[color]};
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
height: ${({ theme }) => theme.spacing(5)};
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background-color: ${({ color, theme }) => theme.tag.text[color]};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: ${({ theme }) => theme.spacing(1)};
|
||||||
|
width: ${({ theme }) => theme.spacing(1)};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledContent = styled.span`
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type StatusProps = {
|
||||||
|
className?: string;
|
||||||
|
color: ThemeColor;
|
||||||
|
text: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Status = ({ className, color, text, onClick }: StatusProps) => (
|
||||||
|
<StyledStatus
|
||||||
|
className={className}
|
||||||
|
color={themeColorSchema.catch('gray').parse(color)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<StyledContent>{text}</StyledContent>
|
||||||
|
</StyledStatus>
|
||||||
|
);
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { expect, jest } from '@storybook/jest';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { mainColorNames, ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
import { CatalogStory } from '~/testing/types';
|
||||||
|
|
||||||
|
import { Status } from '../Status';
|
||||||
|
|
||||||
|
const meta: Meta<typeof Status> = {
|
||||||
|
title: 'UI/Display/Status/Status',
|
||||||
|
component: Status,
|
||||||
|
args: {
|
||||||
|
text: 'Urgent',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof Status>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
color: 'red',
|
||||||
|
onClick: jest.fn(),
|
||||||
|
},
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
play: async ({ canvasElement, args }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const status = canvas.getByRole('heading', { level: 3 });
|
||||||
|
|
||||||
|
await userEvent.click(status);
|
||||||
|
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 Status> = {
|
||||||
|
argTypes: {
|
||||||
|
color: { control: false },
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
catalog: {
|
||||||
|
dimensions: [
|
||||||
|
{
|
||||||
|
name: 'colors',
|
||||||
|
values: mainColorNames,
|
||||||
|
props: (color: ThemeColor) => ({ color }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [CatalogDecorator],
|
||||||
|
};
|
||||||
@ -1,43 +1,23 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
|
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||||
const tagColors = [
|
|
||||||
'green',
|
|
||||||
'turquoise',
|
|
||||||
'sky',
|
|
||||||
'blue',
|
|
||||||
'purple',
|
|
||||||
'pink',
|
|
||||||
'red',
|
|
||||||
'orange',
|
|
||||||
'yellow',
|
|
||||||
'gray',
|
|
||||||
];
|
|
||||||
|
|
||||||
export type TagColor = (typeof tagColors)[number];
|
|
||||||
|
|
||||||
export const castToTagColor = (color: string): TagColor =>
|
|
||||||
tagColors.find((tagColor) => tagColor === color) ?? 'gray';
|
|
||||||
|
|
||||||
const StyledTag = styled.h3<{
|
const StyledTag = styled.h3<{
|
||||||
color: TagColor;
|
color: ThemeColor;
|
||||||
}>`
|
}>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ color, theme }) => theme.tag.background[color]};
|
background: ${({ color, theme }) => theme.tag.background[color]};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
color: ${({ color, theme }) => theme.tag.text[color]};
|
color: ${({ color, theme }) => theme.tag.text[color]};
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: row;
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
height: ${({ theme }) => theme.spacing(5)};
|
height: ${({ theme }) => theme.spacing(5)};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledContent = styled.span`
|
const StyledContent = styled.span`
|
||||||
@ -46,7 +26,7 @@ const StyledContent = styled.span`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export type TagProps = {
|
type TagProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
color: ThemeColor;
|
color: ThemeColor;
|
||||||
text: string;
|
text: string;
|
||||||
@ -56,7 +36,7 @@ export type TagProps = {
|
|||||||
export const Tag = ({ className, color, text, onClick }: TagProps) => (
|
export const Tag = ({ className, color, text, onClick }: TagProps) => (
|
||||||
<StyledTag
|
<StyledTag
|
||||||
className={className}
|
className={className}
|
||||||
color={castToTagColor(color)}
|
color={themeColorSchema.catch('gray').parse(color)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<StyledContent>{text}</StyledContent>
|
<StyledContent>{text}</StyledContent>
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
import { expect } from '@storybook/jest';
|
import { expect, jest } from '@storybook/jest';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { userEvent } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
import { mainColorNames, ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
import { CatalogStory } from '~/testing/types';
|
import { CatalogStory } from '~/testing/types';
|
||||||
|
|
||||||
import { Tag, TagColor } from '../Tag';
|
import { Tag } from '../Tag';
|
||||||
|
|
||||||
const meta: Meta<typeof Tag> = {
|
const meta: Meta<typeof Tag> = {
|
||||||
title: 'UI/Display/Tag/Tag',
|
title: 'UI/Display/Tag/Tag',
|
||||||
component: Tag,
|
component: Tag,
|
||||||
|
args: {
|
||||||
|
text: 'Urgent',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
@ -19,15 +22,14 @@ type Story = StoryObj<typeof Tag>;
|
|||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
text: 'Urgent',
|
|
||||||
color: 'red',
|
color: 'red',
|
||||||
|
onClick: jest.fn(),
|
||||||
},
|
},
|
||||||
argTypes: { onClick: { action: 'clicked' } },
|
|
||||||
decorators: [ComponentDecorator],
|
decorators: [ComponentDecorator],
|
||||||
play: async ({ canvasElement, args }) => {
|
play: async ({ canvasElement, args }) => {
|
||||||
const tag = canvasElement.querySelector('h3');
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
if (!tag) throw new Error('Tag not found');
|
const tag = canvas.getByRole('heading', { level: 3 });
|
||||||
|
|
||||||
await userEvent.click(tag);
|
await userEvent.click(tag);
|
||||||
await expect(args.onClick).toHaveBeenCalled();
|
await expect(args.onClick).toHaveBeenCalled();
|
||||||
@ -46,7 +48,6 @@ export const WithLongText: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Catalog: CatalogStory<Story, typeof Tag> = {
|
export const Catalog: CatalogStory<Story, typeof Tag> = {
|
||||||
args: { text: 'Urgent' },
|
|
||||||
argTypes: {
|
argTypes: {
|
||||||
color: { control: false },
|
color: { control: false },
|
||||||
},
|
},
|
||||||
@ -55,18 +56,7 @@ export const Catalog: CatalogStory<Story, typeof Tag> = {
|
|||||||
dimensions: [
|
dimensions: [
|
||||||
{
|
{
|
||||||
name: 'colors',
|
name: 'colors',
|
||||||
values: [
|
values: mainColorNames,
|
||||||
'green',
|
|
||||||
'turquoise',
|
|
||||||
'sky',
|
|
||||||
'blue',
|
|
||||||
'purple',
|
|
||||||
'pink',
|
|
||||||
'red',
|
|
||||||
'orange',
|
|
||||||
'yellow',
|
|
||||||
'gray',
|
|
||||||
] satisfies TagColor[],
|
|
||||||
props: (color: ThemeColor) => ({ color }),
|
props: (color: ThemeColor) => ({ color }),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const common = {
|
|||||||
sm: '4px',
|
sm: '4px',
|
||||||
md: '8px',
|
md: '8px',
|
||||||
xl: '20px',
|
xl: '20px',
|
||||||
|
pill: '999px',
|
||||||
rounded: '100%',
|
rounded: '100%',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user