From 35023f703fdb19fe32bc605a5cb785913ad07b9a Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Thu, 9 Feb 2023 15:40:39 -0300 Subject: [PATCH 01/15] feat: adding tag component --- src/components/error/error-boundary.tsx | 19 ++++++ src/components/error/styled.ts | 45 +++++++++++++ src/components/icons/icons.tsx | 8 +-- src/components/index.ts | 1 + src/components/tag/index.ts | 1 + src/components/tag/styles.ts | 76 +++++++++++++++++++++ src/components/tag/tag.tsx | 46 +++++++++++++ src/components/utils/validateHexColor.ts | 3 + src/core/types/status.ts | 2 + src/core/types/tag.ts | 10 +++ src/stories/tag.stories.tsx | 85 ++++++++++++++++++++++++ 11 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 src/components/error/error-boundary.tsx create mode 100644 src/components/error/styled.ts create mode 100644 src/components/tag/index.ts create mode 100644 src/components/tag/styles.ts create mode 100644 src/components/tag/tag.tsx create mode 100644 src/components/utils/validateHexColor.ts create mode 100644 src/core/types/status.ts create mode 100644 src/core/types/tag.ts create mode 100644 src/stories/tag.stories.tsx diff --git a/src/components/error/error-boundary.tsx b/src/components/error/error-boundary.tsx new file mode 100644 index 0000000..e36c5a8 --- /dev/null +++ b/src/components/error/error-boundary.tsx @@ -0,0 +1,19 @@ +import IonIcon from '../icons/icons'; +import { ErrorBoundaryStyled } from './styled'; + +export interface ErrorBoundaryProps { + msg: string; +} +const ErrorBoundary = ({ msg }: ErrorBoundaryProps) => { + return ( + + +
+ + {msg} +
+
+ ); +}; + +export default ErrorBoundary; diff --git a/src/components/error/styled.ts b/src/components/error/styled.ts new file mode 100644 index 0000000..98096fe --- /dev/null +++ b/src/components/error/styled.ts @@ -0,0 +1,45 @@ +import stitches from '../../stitches.config'; + +const { styled } = stitches; + +export const ErrorBoundaryStyled = styled('div', { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + padding: '8px 16px 8px 12px', + gap: 8, + + width: 'max-content', + height: 24, + + backgroundColor: '$warning1', + + borderWidth: '1px 1px 1px 8px', + borderStyle: 'solid', + borderColor: '$warning7', + borderRadius: '8px', + + fontSize: 14, + color: '$neutral8', + + svg: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: 8, + + backgroundColor: '$warning7', + fill: '$warning1', + + borderRadius: 8, + }, + + div: { + display: 'flex', + gap: 4, + }, + + label: { + fontWeight: 'bold', + }, +}); diff --git a/src/components/icons/icons.tsx b/src/components/icons/icons.tsx index aa44eed..24f2c48 100644 --- a/src/components/icons/icons.tsx +++ b/src/components/icons/icons.tsx @@ -19,15 +19,15 @@ function pathPurify(iconType: iconType): string { return pathWithoutSvg; } -const defaultSize = 24 +const defaultSize = 24; const Icon = styled('svg', { - height: `${(size: number) => `${ size || defaultSize }px`}`, - width: `${(size: number) => `${ size || defaultSize }px`}`, + height: `${(size: number) => `${size}px`}`, + width: `${(size: number) => `${size}px`}`, fill: `${(color: string) => color || '#282B33'}`, }); -const IonIcon = ({ type, color, size }: IonIconProps) => { +const IonIcon = ({ type, color, size = defaultSize }: IonIconProps) => { const iconPath = pathPurify(type); return ( !label || String(label).trim() === ''; + +const IonTag = ({ + label, + color, + icon, + status, + outline = true, +}: IonTagProps) => { + const isValidColor = color ? validateHexColor(color) : false; + const newColor = status ? undefined : isValidColor ? color : defaultColor; + const hasLabel = validateLabel(label); + + if (hasLabel) { + return ; + } + + return ( + + {icon && } + {label} + + ); +}; + +export default IonTag; diff --git a/src/components/utils/validateHexColor.ts b/src/components/utils/validateHexColor.ts new file mode 100644 index 0000000..59020d3 --- /dev/null +++ b/src/components/utils/validateHexColor.ts @@ -0,0 +1,3 @@ +export const validateHexColor = (color: string): boolean => { + return /^#(?:[0-9a-fA-F]{3,4}){1,2}$/.test(color); +}; diff --git a/src/core/types/status.ts b/src/core/types/status.ts new file mode 100644 index 0000000..2ab0084 --- /dev/null +++ b/src/core/types/status.ts @@ -0,0 +1,2 @@ +export type StatusType = 'success' | 'info' | 'warning' | 'negative'; +export type TagStatus = 'success' | 'info' | 'warning' | 'negative' | 'neutral'; diff --git a/src/core/types/tag.ts b/src/core/types/tag.ts new file mode 100644 index 0000000..1bccabc --- /dev/null +++ b/src/core/types/tag.ts @@ -0,0 +1,10 @@ +import { iconType } from '../../components/icons/svgs/icons'; +import { TagStatus } from './status'; + +export interface IonTagProps { + outline?: boolean; + status?: TagStatus; + color?: string; + label: string; + icon?: iconType; +} diff --git a/src/stories/tag.stories.tsx b/src/stories/tag.stories.tsx new file mode 100644 index 0000000..f62854b --- /dev/null +++ b/src/stories/tag.stories.tsx @@ -0,0 +1,85 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; + +import IonTag from '../components/tag/tag'; +import { IonTagProps } from '../core/types/tag'; + +export default { + title: 'Ion/Data Display/Tag', + component: IonTag, +} as ComponentMeta; + +const Template: ComponentStory = (args: IonTagProps) => ( + +); + +export const tagDefault = Template.bind({}); +tagDefault.storyName = 'type: default'; +tagDefault.args = { + label: 'Exemple Message', +}; + +export const TagWithoutOutline = Template.bind({}); +TagWithoutOutline.storyName = 'tag: outline'; +TagWithoutOutline.args = { + label: 'Exemple Message', + outline: false, +}; + +export const TagWithStatusSuccess = Template.bind({}); +TagWithStatusSuccess.storyName = 'type: success'; +TagWithStatusSuccess.args = { + label: 'tag with status success', + status: 'success', +}; + +export const TagWithStatusWarning = Template.bind({}); +TagWithStatusWarning.storyName = 'type: warning'; +TagWithStatusWarning.args = { + label: 'Exemple Message', + status: 'warning', +}; + +export const TagWithStatusInfo = Template.bind({}); +TagWithStatusInfo.storyName = 'type: info'; +TagWithStatusInfo.args = { + label: 'Exemple Message', + status: 'info', +}; + +export const TagWithStatusNegative = Template.bind({}); +TagWithStatusNegative.storyName = 'type: negative'; +TagWithStatusNegative.args = { + label: 'Exemple Message', + status: 'negative', +}; + +export const TagWithStatusNeutral = Template.bind({}); +TagWithStatusNeutral.storyName = 'type: neutral'; +TagWithStatusNeutral.args = { + label: 'Exemple Message', + status: 'neutral', +}; + +export const TagCustom = Template.bind({}); +TagCustom.storyName = 'type: custom'; +TagCustom.args = { + label: 'Exemple Message', + color: '#7f0dff', +}; + +export const tagWithIcon = Template.bind({}); +tagWithIcon.storyName = 'type: with icon'; +tagWithIcon.args = { + label: 'Exemple Message', + icon: 'check', + status: 'success', + outline: true, +}; + +export const tagError = Template.bind({}); +tagError.storyName = 'type: Error'; +tagError.args = { + label: '', + icon: 'check', + status: 'success', +}; From d7bbbc622ca4bb6dc29250ab579e1f72ada3f849 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Thu, 9 Feb 2023 17:35:11 -0300 Subject: [PATCH 02/15] test: adding tests to tag component --- src/components/error/error-boundary.tsx | 4 +- src/components/icons/icons.tsx | 2 + src/components/tag/tag.test.tsx | 64 +++++++++++++++++++++++++ src/components/tag/tag.tsx | 2 + 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/components/tag/tag.test.tsx diff --git a/src/components/error/error-boundary.tsx b/src/components/error/error-boundary.tsx index e36c5a8..3af5be7 100644 --- a/src/components/error/error-boundary.tsx +++ b/src/components/error/error-boundary.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import IonIcon from '../icons/icons'; import { ErrorBoundaryStyled } from './styled'; @@ -6,7 +8,7 @@ export interface ErrorBoundaryProps { } const ErrorBoundary = ({ msg }: ErrorBoundaryProps) => { return ( - +
diff --git a/src/components/icons/icons.tsx b/src/components/icons/icons.tsx index 24f2c48..c5df815 100644 --- a/src/components/icons/icons.tsx +++ b/src/components/icons/icons.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import DOMPurify from 'dompurify'; import { iconsPaths, iconType } from './svgs/icons'; diff --git a/src/components/tag/tag.test.tsx b/src/components/tag/tag.test.tsx new file mode 100644 index 0000000..2421fa3 --- /dev/null +++ b/src/components/tag/tag.test.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import IonTag from './tag'; +import { IonTagProps } from '../../core/types/tag'; +const defaultTag: IonTagProps = { + label: 'tag label', +}; + +const sut = (props = defaultTag) => render(); +const tagId = 'ion-tag'; +const getTag = () => screen.getByTestId(tagId); + +describe('IonTag', () => { + it('should render default tag ', async () => { + await sut(); + const tag = await getTag(); + expect(tag).toBeTruthy(); + }); + + it('sould rendet tag with label "example tag"', () => { + const label = 'example tag'; + sut({ label: label }); + expect(screen.findByText(label)).toBeTruthy(); + }); + + it.each(['success', 'info', 'warning', 'negative', 'neutral'])( + 'should render tag with status: %s', + (status: any) => { + sut({ ...defaultTag, status: status }); + const tag = getTag(); + expect(tag.className).toContain(`status-${status}`); + } + ); + + it('should not render outline in tag', async () => { + await sut({ ...defaultTag, outline: false }); + const tag = getTag(); + screen.debug(); + expect(tag.className).not.toContain('outiline-false'); + }); + + it('should render outline in tag', async () => { + await sut({ ...defaultTag, outline: true }); + const tag = getTag(); + expect(tag.className).not.toContain('outiline-true'); + }); + + it('should render tag with icon check', async () => { + const iconType = 'check'; + await sut({ ...defaultTag, icon: iconType }); + const icon = screen.getByTestId(`ion-icon-${iconType}`); + expect(icon).toBeTruthy(); + }); + + it('should render ErrorBoundary component when not exist label', () => { + sut({ label: '' }); + // eslint-disable-next-line quotes + const msgError = "Error: Label can't be empty"; + const errorBoundary = screen.getByTestId('ion-error-boundary'); + expect(errorBoundary).toBeTruthy(); + expect(screen.findAllByText(msgError)).toBeTruthy(); + }); +}); diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index a85e398..0c1b007 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import { IonTagProps } from '../../core/types/tag'; import { TagStyle } from './styles'; import IonIcon from '../icons/icons'; From 3ac87875268996a4b8667f2222e187ee170bbf5d Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Thu, 9 Feb 2023 19:06:57 -0300 Subject: [PATCH 03/15] test: adding test custom color tag --- src/components/tag/tag.test.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/tag/tag.test.tsx b/src/components/tag/tag.test.tsx index 2421fa3..50de9c7 100644 --- a/src/components/tag/tag.test.tsx +++ b/src/components/tag/tag.test.tsx @@ -18,7 +18,7 @@ describe('IonTag', () => { expect(tag).toBeTruthy(); }); - it('sould rendet tag with label "example tag"', () => { + it('should render tag with label "example tag"', () => { const label = 'example tag'; sut({ label: label }); expect(screen.findByText(label)).toBeTruthy(); @@ -37,13 +37,13 @@ describe('IonTag', () => { await sut({ ...defaultTag, outline: false }); const tag = getTag(); screen.debug(); - expect(tag.className).not.toContain('outiline-false'); + expect(tag.className).not.toContain('outline-false'); }); it('should render outline in tag', async () => { await sut({ ...defaultTag, outline: true }); const tag = getTag(); - expect(tag.className).not.toContain('outiline-true'); + expect(tag.className).toContain('outline-true'); }); it('should render tag with icon check', async () => { @@ -61,4 +61,18 @@ describe('IonTag', () => { expect(errorBoundary).toBeTruthy(); expect(screen.findAllByText(msgError)).toBeTruthy(); }); + + it('should render tag whit color custom', () => { + sut({ ...defaultTag, color: '#AADD00' }); + const tag = getTag(); + screen.debug(); + expect(tag.className).not.toContain('status'); + }); + + it('should render the tag the same as it has a custom color', () => { + sut({ ...defaultTag, status: 'info', color: '#AADD00' }); + const tag = getTag(); + screen.debug(); + expect(tag.className).toContain('status-info'); + }); }); From d340f3a24098da7e0a78edb00fee368f4dc7c0e8 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Thu, 9 Feb 2023 23:22:30 -0300 Subject: [PATCH 04/15] test: removing debugs --- src/components/tag/tag.test.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/tag/tag.test.tsx b/src/components/tag/tag.test.tsx index 50de9c7..f8a75e1 100644 --- a/src/components/tag/tag.test.tsx +++ b/src/components/tag/tag.test.tsx @@ -36,7 +36,6 @@ describe('IonTag', () => { it('should not render outline in tag', async () => { await sut({ ...defaultTag, outline: false }); const tag = getTag(); - screen.debug(); expect(tag.className).not.toContain('outline-false'); }); @@ -65,14 +64,12 @@ describe('IonTag', () => { it('should render tag whit color custom', () => { sut({ ...defaultTag, color: '#AADD00' }); const tag = getTag(); - screen.debug(); expect(tag.className).not.toContain('status'); }); it('should render the tag the same as it has a custom color', () => { sut({ ...defaultTag, status: 'info', color: '#AADD00' }); const tag = getTag(); - screen.debug(); expect(tag.className).toContain('status-info'); }); }); From 96a6dcc2d845eb51c1b88b949a97ceda638a047c Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Thu, 9 Feb 2023 23:24:31 -0300 Subject: [PATCH 05/15] style: improve style object --- src/components/tag/styles.ts | 43 +++++++++++------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/src/components/tag/styles.ts b/src/components/tag/styles.ts index 5f9f1fa..b4b3f15 100644 --- a/src/components/tag/styles.ts +++ b/src/components/tag/styles.ts @@ -2,6 +2,14 @@ import stitches from '../../stitches.config'; const { styled } = stitches; +const setColors = (bgColor: string, color: string) => ({ + backgroundColor: bgColor, + color: color, + svg: { + fill: color, + }, +}); + export const TagStyle = styled('div', { display: 'flex', flexDirection: 'row', @@ -27,44 +35,19 @@ export const TagStyle = styled('div', { variants: { status: { success: { - backgroundColor: '$positive1', - color: '$positive7', - - svg: { - fill: '$positive7', - }, + ...setColors('$positive1', '$positive7'), }, info: { - backgroundColor: '$info1', - color: '$info7', - - svg: { - fill: '$info7', - }, + ...setColors('$info1', '$info7'), }, warning: { - backgroundColor: '$warning1', - color: '$warning7', - - svg: { - fill: '$warning7', - }, + ...setColors('$warning1', '$warning7'), }, negative: { - backgroundColor: '$negative1', - color: '$negative7', - - svg: { - fill: '$negative7', - }, + ...setColors('$negative1', '$negative7'), }, neutral: { - backgroundColor: '$neutral2', - color: '$neutral7', - - svg: { - fill: '$neutral7', - }, + ...setColors('$neutral2', '$neutral7'), }, }, outline: { From 51185a0002d36a91fbb419bb0f1c798db4e923d9 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Fri, 10 Feb 2023 00:52:33 -0300 Subject: [PATCH 06/15] test: adding test icon component --- src/components/icons/icons.test.tsx | 37 +++++++++++++++++++++++++++++ src/components/icons/icons.tsx | 9 +++---- 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/components/icons/icons.test.tsx diff --git a/src/components/icons/icons.test.tsx b/src/components/icons/icons.test.tsx new file mode 100644 index 0000000..941ca9d --- /dev/null +++ b/src/components/icons/icons.test.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import IonIcon from './icons'; +import { IonIconProps } from './icons'; +import { iconType } from './svgs/icons'; + +const defaultIcon: IonIconProps = { + type: 'pencil', +}; + +const sut = (props = defaultIcon) => render(); +const getIcon = (type: iconType) => screen.getByTestId(`ion-icon-${type}`); + +describe('IonIcon', () => { + it('should render default icon of type pencil', () => { + sut(); + const icon = getIcon('pencil'); + expect(icon).toBeTruthy(); + expect(icon.getAttribute('width')).toBe('24'); + expect(icon.getAttribute('height')).toBe('24'); + expect(icon.getAttribute('fill')).toBe('#282B33'); + }); + + it('should render icon with size 12', () => { + sut({ ...defaultIcon, size: 12 }); + const icon = getIcon('pencil'); + expect(icon.getAttribute('width')).toBe('12'); + expect(icon.getAttribute('height')).toBe('12'); + }); + + it('should render icon with color purple', () => { + sut({ ...defaultIcon, color: 'purple' }); + const icon = getIcon('pencil'); + expect(icon.getAttribute('fill')).toBe('purple'); + }); +}); diff --git a/src/components/icons/icons.tsx b/src/components/icons/icons.tsx index c5df815..0fab113 100644 --- a/src/components/icons/icons.tsx +++ b/src/components/icons/icons.tsx @@ -22,12 +22,9 @@ function pathPurify(iconType: iconType): string { } const defaultSize = 24; +const defaultColor = '#282B33'; -const Icon = styled('svg', { - height: `${(size: number) => `${size}px`}`, - width: `${(size: number) => `${size}px`}`, - fill: `${(color: string) => color || '#282B33'}`, -}); +const Icon = styled('svg', {}); const IonIcon = ({ type, color, size = defaultSize }: IonIconProps) => { const iconPath = pathPurify(type); @@ -37,7 +34,7 @@ const IonIcon = ({ type, color, size = defaultSize }: IonIconProps) => { viewBox="0 0 24 24" height={size} width={size} - fill={color} + fill={color || defaultColor} dangerouslySetInnerHTML={{ __html: iconPath, }} From abece6dc252f753a0b650b018985033ef519da49 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Fri, 10 Feb 2023 09:27:12 -0300 Subject: [PATCH 07/15] refactor: reduce complexity --- src/components/tag/tag.tsx | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 0c1b007..38de13c 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -6,6 +6,7 @@ import IonIcon from '../icons/icons'; import { validateHexColor } from '../utils/validateHexColor'; import ErrorBoundary from '../error/error-boundary'; +import { TagStatus } from '../../core/types/status'; const iconSize = 12; const defaultColor = '#505566'; @@ -13,6 +14,26 @@ const lighteningFactor = '1A'; const validateLabel = (label: string) => !label || String(label).trim() === ''; +const newColor = (color: string) => ({ + backgroundColor: color + lighteningFactor, + color: color, + fill: color, +}); + +const getColorObject = (status?: TagStatus, color?: string) => { + if (status) { + return {}; + } + + if (!color || !validateHexColor(color)) { + return { ...newColor(defaultColor) }; + } + + return { + ...newColor(color), + }; +}; + const IonTag = ({ label, color, @@ -20,11 +41,9 @@ const IonTag = ({ status, outline = true, }: IonTagProps) => { - const isValidColor = color ? validateHexColor(color) : false; - const newColor = status ? undefined : isValidColor ? color : defaultColor; - const hasLabel = validateLabel(label); + const invalidLabel = validateLabel(label); - if (hasLabel) { + if (invalidLabel) { return ; } @@ -33,11 +52,7 @@ const IonTag = ({ data-testid="ion-tag" status={status} outline={outline} - css={{ - backgroundColor: newColor + lighteningFactor, - color: newColor, - fill: newColor, - }} + css={{ ...getColorObject(status, color) }} > {icon && } {label} From b84a42fd6aa510cca2fe9fa0074dbb510512bd7e Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Fri, 10 Feb 2023 11:08:21 -0300 Subject: [PATCH 08/15] refactor: improving code error-boundary component --- src/components/error/error-boundary.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/error/error-boundary.tsx b/src/components/error/error-boundary.tsx index 3af5be7..7d13d8b 100644 --- a/src/components/error/error-boundary.tsx +++ b/src/components/error/error-boundary.tsx @@ -6,10 +6,13 @@ import { ErrorBoundaryStyled } from './styled'; export interface ErrorBoundaryProps { msg: string; } +const sizeIcon = 16; +const iconType = 'info'; + const ErrorBoundary = ({ msg }: ErrorBoundaryProps) => { return ( - +
{msg} From 2c27b7eb115f533f56b67138d91a08f5f61d72ae Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Fri, 10 Feb 2023 11:18:29 -0300 Subject: [PATCH 09/15] test: improving tests --- src/components/tag/tag.test.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/tag/tag.test.tsx b/src/components/tag/tag.test.tsx index f8a75e1..ef47c11 100644 --- a/src/components/tag/tag.test.tsx +++ b/src/components/tag/tag.test.tsx @@ -11,17 +11,19 @@ const sut = (props = defaultTag) => render(); const tagId = 'ion-tag'; const getTag = () => screen.getByTestId(tagId); +const customColor = '#AADD00'; + describe('IonTag', () => { - it('should render default tag ', async () => { - await sut(); - const tag = await getTag(); + it('should render default tag ', () => { + sut(); + const tag = getTag(); expect(tag).toBeTruthy(); }); it('should render tag with label "example tag"', () => { - const label = 'example tag'; - sut({ label: label }); - expect(screen.findByText(label)).toBeTruthy(); + const customLabel = 'example tag'; + sut({ label: customLabel }); + expect(screen.findByText(customLabel)).toBeTruthy(); }); it.each(['success', 'info', 'warning', 'negative', 'neutral'])( @@ -62,14 +64,15 @@ describe('IonTag', () => { }); it('should render tag whit color custom', () => { - sut({ ...defaultTag, color: '#AADD00' }); + sut({ ...defaultTag, color: customColor }); const tag = getTag(); expect(tag.className).not.toContain('status'); }); it('should render the tag the same as it has a custom color', () => { - sut({ ...defaultTag, status: 'info', color: '#AADD00' }); + const statusInfo = 'status-info'; + sut({ ...defaultTag, status: 'info', color: customColor }); const tag = getTag(); - expect(tag.className).toContain('status-info'); + expect(tag.className).toContain(statusInfo); }); }); From 6b572d55fe2cc67d9a6df60e379cd05ff421214a Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Fri, 10 Feb 2023 12:35:46 -0300 Subject: [PATCH 10/15] docs: move stories to folder and improve examples --- src/stories/{ => tag}/tag.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/stories/{ => tag}/tag.stories.tsx (92%) diff --git a/src/stories/tag.stories.tsx b/src/stories/tag/tag.stories.tsx similarity index 92% rename from src/stories/tag.stories.tsx rename to src/stories/tag/tag.stories.tsx index f62854b..c09553e 100644 --- a/src/stories/tag.stories.tsx +++ b/src/stories/tag/tag.stories.tsx @@ -1,7 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import IonTag from '../components/tag/tag'; -import { IonTagProps } from '../core/types/tag'; +import IonTag from '../../components/tag/tag'; +import { IonTagProps } from '../../core/types/tag'; export default { title: 'Ion/Data Display/Tag', @@ -19,7 +19,7 @@ tagDefault.args = { }; export const TagWithoutOutline = Template.bind({}); -TagWithoutOutline.storyName = 'tag: outline'; +TagWithoutOutline.storyName = 'type: without outline'; TagWithoutOutline.args = { label: 'Exemple Message', outline: false, From ecc14e822c4e7c4540d393774293a18191a95f2a Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Mon, 13 Feb 2023 12:28:25 -0300 Subject: [PATCH 11/15] refactor: improving code --- src/components/tag/tag.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 38de13c..7b8365a 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -12,7 +12,8 @@ const iconSize = 12; const defaultColor = '#505566'; const lighteningFactor = '1A'; -const validateLabel = (label: string) => !label || String(label).trim() === ''; +const validateLabel = (label: string) => + label || !(String(label).trim() === ''); const newColor = (color: string) => ({ backgroundColor: color + lighteningFactor, @@ -41,9 +42,9 @@ const IonTag = ({ status, outline = true, }: IonTagProps) => { - const invalidLabel = validateLabel(label); + const isValidLabel = validateLabel(label); - if (invalidLabel) { + if (!isValidLabel) { return ; } From 52083b8e1185c85b5326466726702827c0a0efb3 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Mon, 13 Feb 2023 12:36:43 -0300 Subject: [PATCH 12/15] fix: fix validateLabel function --- src/components/tag/tag.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 7b8365a..17f91c9 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -13,7 +13,7 @@ const defaultColor = '#505566'; const lighteningFactor = '1A'; const validateLabel = (label: string) => - label || !(String(label).trim() === ''); + label && !(String(label).trim() === ''); const newColor = (color: string) => ({ backgroundColor: color + lighteningFactor, From 7b6aa9685aaaefd3cdde6a9e56b6151789becce2 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Mon, 13 Feb 2023 13:48:14 -0300 Subject: [PATCH 13/15] refactor: improving code --- src/components/tag/tag.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 17f91c9..150d4e5 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -12,7 +12,7 @@ const iconSize = 12; const defaultColor = '#505566'; const lighteningFactor = '1A'; -const validateLabel = (label: string) => +const isValidateLabel = (label: string) => label && !(String(label).trim() === ''); const newColor = (color: string) => ({ @@ -42,9 +42,7 @@ const IonTag = ({ status, outline = true, }: IonTagProps) => { - const isValidLabel = validateLabel(label); - - if (!isValidLabel) { + if (!isValidateLabel(label)) { return ; } From e0d7dd706d87ed73d1dc57d0aaafa8835dc45972 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Mon, 13 Feb 2023 13:49:51 -0300 Subject: [PATCH 14/15] refactor: rename function --- src/components/tag/tag.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 150d4e5..0f5aaab 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -12,8 +12,7 @@ const iconSize = 12; const defaultColor = '#505566'; const lighteningFactor = '1A'; -const isValidateLabel = (label: string) => - label && !(String(label).trim() === ''); +const isValidLabel = (label: string) => label && !(String(label).trim() === ''); const newColor = (color: string) => ({ backgroundColor: color + lighteningFactor, @@ -42,7 +41,7 @@ const IonTag = ({ status, outline = true, }: IonTagProps) => { - if (!isValidateLabel(label)) { + if (!isValidLabel(label)) { return ; } From e671387c38eeb22d4d60d4b3cef5bbac2e847d47 Mon Sep 17 00:00:00 2001 From: Deeved Hiuston Date: Mon, 13 Feb 2023 16:45:20 -0300 Subject: [PATCH 15/15] refactor: improving code --- src/components/tag/tag.test.tsx | 57 ++++++++++++++++----------------- src/components/tag/tag.tsx | 12 +++++-- src/core/types/tag.ts | 10 ------ src/stories/tag/tag.stories.tsx | 9 +++--- 4 files changed, 41 insertions(+), 47 deletions(-) delete mode 100644 src/core/types/tag.ts diff --git a/src/components/tag/tag.test.tsx b/src/components/tag/tag.test.tsx index ef47c11..3bfa52d 100644 --- a/src/components/tag/tag.test.tsx +++ b/src/components/tag/tag.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import IonTag from './tag'; -import { IonTagProps } from '../../core/types/tag'; +import IonTag, { IonTagProps } from './tag'; +import { StatusType } from '../../core/types/status'; + const defaultTag: IonTagProps = { label: 'tag label', }; @@ -16,35 +17,34 @@ const customColor = '#AADD00'; describe('IonTag', () => { it('should render default tag ', () => { sut(); - const tag = getTag(); - expect(tag).toBeTruthy(); + expect(getTag()).toBeTruthy(); }); - it('should render tag with label "example tag"', () => { + it('should render tag with label "example tag"', async () => { const customLabel = 'example tag'; - sut({ label: customLabel }); + await sut({ label: customLabel }); expect(screen.findByText(customLabel)).toBeTruthy(); }); - it.each(['success', 'info', 'warning', 'negative', 'neutral'])( - 'should render tag with status: %s', - (status: any) => { - sut({ ...defaultTag, status: status }); - const tag = getTag(); - expect(tag.className).toContain(`status-${status}`); - } - ); + it.each([ + 'success', + 'info', + 'warning', + 'negative', + 'neutral', + ] as StatusType[])('should render tag with status: %s', (status) => { + sut({ ...defaultTag, status: status }); + expect(getTag().className).toContain(`status-${status}`); + }); it('should not render outline in tag', async () => { await sut({ ...defaultTag, outline: false }); - const tag = getTag(); - expect(tag.className).not.toContain('outline-false'); + expect(getTag().className).not.toContain('outline-false'); }); it('should render outline in tag', async () => { await sut({ ...defaultTag, outline: true }); - const tag = getTag(); - expect(tag.className).toContain('outline-true'); + expect(getTag().className).toContain('outline-true'); }); it('should render tag with icon check', async () => { @@ -54,25 +54,22 @@ describe('IonTag', () => { expect(icon).toBeTruthy(); }); - it('should render ErrorBoundary component when not exist label', () => { + it('should render ErrorBoundary component when not exist label', async () => { sut({ label: '' }); - // eslint-disable-next-line quotes - const msgError = "Error: Label can't be empty"; + const msgError = 'Label cannot be empty'; const errorBoundary = screen.getByTestId('ion-error-boundary'); expect(errorBoundary).toBeTruthy(); - expect(screen.findAllByText(msgError)).toBeTruthy(); + expect(await screen.findByText(msgError)).toBeTruthy(); }); - it('should render tag whit color custom', () => { - sut({ ...defaultTag, color: customColor }); - const tag = getTag(); - expect(tag.className).not.toContain('status'); + it('should render tag with custom color', async () => { + await sut({ ...defaultTag, color: customColor }); + expect(getTag().className).not.toContain('status'); }); - it('should render the tag the same as it has a custom color', () => { + it('should render the tag the same as it has a custom color', async () => { const statusInfo = 'status-info'; - sut({ ...defaultTag, status: 'info', color: customColor }); - const tag = getTag(); - expect(tag.className).toContain(statusInfo); + await sut({ ...defaultTag, status: 'info', color: customColor }); + expect(getTag().className).toContain(statusInfo); }); }); diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 0f5aaab..51f3daf 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -1,12 +1,20 @@ import React from 'react'; -import { IonTagProps } from '../../core/types/tag'; import { TagStyle } from './styles'; import IonIcon from '../icons/icons'; import { validateHexColor } from '../utils/validateHexColor'; import ErrorBoundary from '../error/error-boundary'; import { TagStatus } from '../../core/types/status'; +import { iconType } from '../icons/svgs/icons'; + +export interface IonTagProps { + outline?: boolean; + status?: TagStatus; + color?: string; + label: string; + icon?: iconType; +} const iconSize = 12; const defaultColor = '#505566'; @@ -42,7 +50,7 @@ const IonTag = ({ outline = true, }: IonTagProps) => { if (!isValidLabel(label)) { - return ; + return ; } return ( diff --git a/src/core/types/tag.ts b/src/core/types/tag.ts deleted file mode 100644 index 1bccabc..0000000 --- a/src/core/types/tag.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { iconType } from '../../components/icons/svgs/icons'; -import { TagStatus } from './status'; - -export interface IonTagProps { - outline?: boolean; - status?: TagStatus; - color?: string; - label: string; - icon?: iconType; -} diff --git a/src/stories/tag/tag.stories.tsx b/src/stories/tag/tag.stories.tsx index c09553e..96b72f7 100644 --- a/src/stories/tag/tag.stories.tsx +++ b/src/stories/tag/tag.stories.tsx @@ -1,7 +1,6 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import IonTag from '../../components/tag/tag'; -import { IonTagProps } from '../../core/types/tag'; +import IonTag, { IonTagProps } from '../../components/tag/tag'; export default { title: 'Ion/Data Display/Tag', @@ -60,9 +59,9 @@ TagWithStatusNeutral.args = { status: 'neutral', }; -export const TagCustom = Template.bind({}); -TagCustom.storyName = 'type: custom'; -TagCustom.args = { +export const TagCustomColor = Template.bind({}); +TagCustomColor.storyName = 'type: custom'; +TagCustomColor.args = { label: 'Exemple Message', color: '#7f0dff', };