From 5c0ad30186b808e774aebb8510383803a8148ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tha=C3=AFs?= Date: Tue, 5 Dec 2023 11:07:51 +0100 Subject: [PATCH] feat: add Status component (#2838) Closes #2820 --- .../ui/display/pill/components/SoonPill.tsx | 2 +- .../ui/display/status/components/Status.tsx | 55 ++++++++++++++++ .../components/__stories__/Status.stories.tsx | 66 +++++++++++++++++++ .../modules/ui/display/tag/components/Tag.tsx | 30 ++------- .../components/__stories__/Tag.stories.tsx | 32 ++++----- .../src/modules/ui/theme/constants/border.ts | 1 + 6 files changed, 139 insertions(+), 47 deletions(-) create mode 100644 front/src/modules/ui/display/status/components/Status.tsx create mode 100644 front/src/modules/ui/display/status/components/__stories__/Status.stories.tsx diff --git a/front/src/modules/ui/display/pill/components/SoonPill.tsx b/front/src/modules/ui/display/pill/components/SoonPill.tsx index e7ec25691..2a6b43408 100644 --- a/front/src/modules/ui/display/pill/components/SoonPill.tsx +++ b/front/src/modules/ui/display/pill/components/SoonPill.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; const StyledSoonPill = styled.span` align-items: center; background: ${({ theme }) => theme.background.transparent.light}; - border-radius: 50px; + border-radius: ${({ theme }) => theme.border.radius.pill}; color: ${({ theme }) => theme.font.color.light}; display: inline-block; font-size: ${({ theme }) => theme.font.size.xs}; diff --git a/front/src/modules/ui/display/status/components/Status.tsx b/front/src/modules/ui/display/status/components/Status.tsx new file mode 100644 index 000000000..4ba436c92 --- /dev/null +++ b/front/src/modules/ui/display/status/components/Status.tsx @@ -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) => ( + + {text} + +); diff --git a/front/src/modules/ui/display/status/components/__stories__/Status.stories.tsx b/front/src/modules/ui/display/status/components/__stories__/Status.stories.tsx new file mode 100644 index 000000000..c1b21b2e4 --- /dev/null +++ b/front/src/modules/ui/display/status/components/__stories__/Status.stories.tsx @@ -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 = { + title: 'UI/Display/Status/Status', + component: Status, + args: { + text: 'Urgent', + }, +}; + +export default meta; +type Story = StoryObj; + +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 = { + argTypes: { + color: { control: false }, + }, + parameters: { + catalog: { + dimensions: [ + { + name: 'colors', + values: mainColorNames, + props: (color: ThemeColor) => ({ color }), + }, + ], + }, + }, + decorators: [CatalogDecorator], +}; diff --git a/front/src/modules/ui/display/tag/components/Tag.tsx b/front/src/modules/ui/display/tag/components/Tag.tsx index 70a63a9e3..7019f4f06 100644 --- a/front/src/modules/ui/display/tag/components/Tag.tsx +++ b/front/src/modules/ui/display/tag/components/Tag.tsx @@ -1,43 +1,23 @@ import styled from '@emotion/styled'; import { ThemeColor } from '@/ui/theme/constants/colors'; - -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'; +import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema'; const StyledTag = styled.h3<{ - color: TagColor; + color: ThemeColor; }>` 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; - flex-direction: row; font-size: ${({ theme }) => theme.font.size.md}; font-style: normal; font-weight: ${({ theme }) => theme.font.weight.regular}; - gap: ${({ theme }) => theme.spacing(2)}; height: ${({ theme }) => theme.spacing(5)}; margin: 0; overflow: hidden; - padding-left: ${({ theme }) => theme.spacing(2)}; - padding-right: ${({ theme }) => theme.spacing(2)}; + padding: 0 ${({ theme }) => theme.spacing(2)}; `; const StyledContent = styled.span` @@ -46,7 +26,7 @@ const StyledContent = styled.span` white-space: nowrap; `; -export type TagProps = { +type TagProps = { className?: string; color: ThemeColor; text: string; @@ -56,7 +36,7 @@ export type TagProps = { export const Tag = ({ className, color, text, onClick }: TagProps) => ( {text} diff --git a/front/src/modules/ui/display/tag/components/__stories__/Tag.stories.tsx b/front/src/modules/ui/display/tag/components/__stories__/Tag.stories.tsx index e376a056e..b6bcf3fcb 100644 --- a/front/src/modules/ui/display/tag/components/__stories__/Tag.stories.tsx +++ b/front/src/modules/ui/display/tag/components/__stories__/Tag.stories.tsx @@ -1,17 +1,20 @@ -import { expect } from '@storybook/jest'; +import { expect, jest } from '@storybook/jest'; 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 { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { CatalogStory } from '~/testing/types'; -import { Tag, TagColor } from '../Tag'; +import { Tag } from '../Tag'; const meta: Meta = { title: 'UI/Display/Tag/Tag', component: Tag, + args: { + text: 'Urgent', + }, }; export default meta; @@ -19,15 +22,14 @@ type Story = StoryObj; export const Default: Story = { args: { - text: 'Urgent', color: 'red', + onClick: jest.fn(), }, - argTypes: { onClick: { action: 'clicked' } }, decorators: [ComponentDecorator], 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 expect(args.onClick).toHaveBeenCalled(); @@ -46,7 +48,6 @@ export const WithLongText: Story = { }; export const Catalog: CatalogStory = { - args: { text: 'Urgent' }, argTypes: { color: { control: false }, }, @@ -55,18 +56,7 @@ export const Catalog: CatalogStory = { dimensions: [ { name: 'colors', - values: [ - 'green', - 'turquoise', - 'sky', - 'blue', - 'purple', - 'pink', - 'red', - 'orange', - 'yellow', - 'gray', - ] satisfies TagColor[], + values: mainColorNames, props: (color: ThemeColor) => ({ color }), }, ], diff --git a/front/src/modules/ui/theme/constants/border.ts b/front/src/modules/ui/theme/constants/border.ts index 48895b949..1fd0e7571 100644 --- a/front/src/modules/ui/theme/constants/border.ts +++ b/front/src/modules/ui/theme/constants/border.ts @@ -6,6 +6,7 @@ const common = { sm: '4px', md: '8px', xl: '20px', + pill: '999px', rounded: '100%', }, };