Skip to content

Commit 9bfb502

Browse files
author
Hector Arce De Las Heras
committed
Create TagV2 component and deprecate original Tag component
This commit introduces a new component, TagV2, which is intended to replace the original Tag component. The original Tag component has been marked as deprecated. The new TagV2 component includes enhancements and updates that improve its functionality and usability. Users are encouraged to transition to using TagV2 in their future developments.
1 parent bf93090 commit 9bfb502

File tree

17 files changed

+461
-0
lines changed

17 files changed

+461
-0
lines changed

src/components/tag/tag.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ const TagBoundary = <
5858
</ErrorBoundary>
5959
);
6060

61+
/**
62+
* @deprecated Try TagV2 component that will override this component in the next major
63+
*/
6164
const Tag = React.forwardRef(TagBoundary) as <V, S>(
6265
p: ITag<V, S> & {
6366
ref?: React.ForwardedRef<HTMLDivElement> | undefined | null;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as React from 'react';
2+
3+
import { axe } from 'jest-axe';
4+
5+
import { ICONS } from '@/assets';
6+
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
7+
8+
import { Tag } from '../tag';
9+
10+
const mockProps = {
11+
variant: 'HEALTHY',
12+
icon: { icon: ICONS.ICON_PLACEHOLDER },
13+
label: { content: 'LABEL' },
14+
};
15+
16+
describe('Tag component', () => {
17+
it('Should render tag component', async () => {
18+
const { container, getByText } = renderProvider(<Tag {...mockProps} />);
19+
20+
const tagLabel = getByText(mockProps.label.content);
21+
22+
expect(tagLabel).toBeInTheDocument();
23+
24+
const results = await axe(container);
25+
expect(container).toHTMLValidate();
26+
expect(results).toHaveNoViolations();
27+
});
28+
});

src/components/tagV2/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export type {
2+
ITag as ITagV2,
3+
TagStylesType as TagStylesTypeV2,
4+
TagPropsStylesType as TagPropsStylesTypeV2,
5+
TagLabelType as TagLabelTypeV2,
6+
} from './types';
7+
8+
export { Tag as TagV2 } from './tag';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { CATEGORY_CONTROL } from '@/constants';
2+
import { IThemeObjectVariants } from '@/designSystem/themesObject';
3+
import { ArgTypesReturn } from '@/types';
4+
5+
export const argtypes = (
6+
variantsObject: IThemeObjectVariants,
7+
themeSelected: string
8+
): ArgTypesReturn => {
9+
return {
10+
variant: {
11+
description: 'Select tag variant type',
12+
type: { name: 'string', required: true },
13+
control: { type: 'select' },
14+
options: Object.keys(variantsObject[themeSelected].TagVariantTypeV2 || {}),
15+
table: {
16+
type: {
17+
summary: 'string',
18+
},
19+
category: CATEGORY_CONTROL.MODIFIERS,
20+
},
21+
},
22+
icon: {
23+
description: 'Object with the icon properties',
24+
type: { name: 'object' },
25+
control: { type: 'object' },
26+
table: {
27+
type: {
28+
summary: 'IElementOrIcon',
29+
},
30+
category: CATEGORY_CONTROL.CONTENT,
31+
},
32+
},
33+
label: {
34+
description: 'Object with the label properties',
35+
control: { type: 'object' },
36+
type: { name: 'object' },
37+
table: {
38+
type: {
39+
summary: 'TagLabelType',
40+
},
41+
category: CATEGORY_CONTROL.CONTENT,
42+
},
43+
},
44+
dataTestId: {
45+
description: 'Test id',
46+
type: { name: 'string' },
47+
table: {
48+
type: {
49+
summary: 'string',
50+
},
51+
defaultValue: { summary: 'tag' },
52+
category: CATEGORY_CONTROL.TESTING,
53+
},
54+
},
55+
ctv: {
56+
description: 'Object used for update variant styles',
57+
type: { name: 'object' },
58+
control: { type: 'object' },
59+
table: {
60+
type: {
61+
summary: 'object',
62+
},
63+
category: CATEGORY_CONTROL.CUSTOMIZATION,
64+
},
65+
},
66+
};
67+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
3+
import { ICONS } from '@/assets';
4+
import { STYLES_NAME } from '@/constants';
5+
import { themesObject, variantsObject } from '@/designSystem/themesObject';
6+
7+
import { Tag as Story } from '../tag';
8+
import { argtypes } from './argtypes';
9+
10+
const themeSelected = localStorage.getItem('themeSelected') || 'kubit';
11+
12+
const meta = {
13+
title: 'Components/Status/TagV2',
14+
component: Story,
15+
parameters: {
16+
layout: 'centered',
17+
},
18+
tags: ['autodocs'],
19+
argTypes: argtypes(variantsObject, themeSelected),
20+
} satisfies Meta<typeof Story>;
21+
22+
export default meta;
23+
24+
type Story = StoryObj<typeof meta> & { args: { themeArgs?: object } };
25+
26+
const commonTagProps = {
27+
variant: Object.values(variantsObject[themeSelected].TagVariantTypeV2 || {})[0] as string,
28+
icon: { icon: ICONS.ICON_PLACEHOLDER },
29+
label: { content: 'LABEL' },
30+
};
31+
32+
export const Tag: Story = {
33+
args: {
34+
...commonTagProps,
35+
themeArgs: themesObject[themeSelected][STYLES_NAME.TAG_V2],
36+
},
37+
};
38+
39+
export const TagWithCtv: Story = {
40+
args: {
41+
...commonTagProps,
42+
ctv: {
43+
icon: {
44+
color: 'red',
45+
},
46+
},
47+
},
48+
};

src/components/tagV2/tag.styled.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styled from 'styled-components';
2+
3+
import { getStyles } from '@/utils/getStyles/getStyles';
4+
5+
import { TagPropsStylesType } from './types';
6+
7+
export const TagContainerStyled = styled.div<{ styles: TagPropsStylesType }>`
8+
${({ styles }) => getStyles(styles.container)};
9+
`;

src/components/tagV2/tag.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from 'react';
2+
3+
import { STYLES_NAME } from '@/constants';
4+
import { useStyles } from '@/hooks/useStyles/useStyles';
5+
import { ErrorBoundary, FallbackComponent } from '@/provider/errorBoundary';
6+
7+
import { TagStandAlone } from './tagStandAlone';
8+
import type { ITag, ITagStandAlone, TagPropsStylesType } from './types';
9+
10+
const TagComponent = React.forwardRef(
11+
<V extends string>(
12+
{ variant, ctv, ...props }: ITag<V>,
13+
ref: React.ForwardedRef<HTMLDivElement> | undefined | null
14+
): JSX.Element => {
15+
const styles: TagPropsStylesType = useStyles<TagPropsStylesType>(
16+
STYLES_NAME.TAG_V2,
17+
variant,
18+
ctv
19+
);
20+
21+
return <TagStandAlone {...props} ref={ref} styles={styles} />;
22+
}
23+
);
24+
TagComponent.displayName = 'TagComponent';
25+
26+
const TagBoundary = <V extends string>(
27+
props: ITag<V>,
28+
ref: React.ForwardedRef<HTMLDivElement> | undefined | null
29+
): JSX.Element => (
30+
<ErrorBoundary
31+
fallBackComponent={
32+
<FallbackComponent>
33+
<TagStandAlone {...(props as unknown as ITagStandAlone)} ref={ref} />
34+
</FallbackComponent>
35+
}
36+
>
37+
<TagComponent {...props} ref={ref} />
38+
</ErrorBoundary>
39+
);
40+
41+
const Tag = React.forwardRef(TagBoundary) as <V, S>(
42+
props: ITag<V> & React.RefAttributes<HTMLDivElement>
43+
) => JSX.Element;
44+
45+
/**
46+
* @description
47+
* Tag component is used to highlight or categorize important information.
48+
*/
49+
export { Tag };
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from 'react';
2+
3+
import { ElementOrIcon } from '@/components/elementOrIcon';
4+
import { Text } from '@/components/text';
5+
import { TextComponentType } from '@/components/text/types';
6+
import { pickAriaProps } from '@/utils/aria/aria';
7+
8+
import { TagContainerStyled } from './tag.styled';
9+
import { ITagStandAlone } from './types';
10+
11+
const TagStandAloneComponent = (
12+
{ dataTestId = 'tag', ...props }: ITagStandAlone,
13+
ref: React.ForwardedRef<HTMLDivElement> | undefined | null
14+
): JSX.Element => {
15+
const ariaProps = pickAriaProps(props);
16+
return (
17+
<TagContainerStyled ref={ref} data-testid={dataTestId} styles={props.styles} {...ariaProps}>
18+
<ElementOrIcon customIconStyles={props.styles.icon} {...props.icon} />
19+
<Text
20+
component={TextComponentType.SPAN}
21+
customTypography={props.styles.label}
22+
{...props.label}
23+
>
24+
{props.label?.content}
25+
</Text>
26+
</TagContainerStyled>
27+
);
28+
};
29+
30+
/**
31+
* @description
32+
* Tag component is used to highlight or categorize important information.
33+
*/
34+
export const TagStandAlone = React.forwardRef(TagStandAloneComponent);

src/components/tagV2/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './tag';
2+
export * from './tagTheme';

src/components/tagV2/types/tag.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { IElementOrIcon } from '@/components/elementOrIcon';
2+
import { IText } from '@/components/text';
3+
import { CustomTokenTypes } from '@/types';
4+
5+
import { TagPropsStylesType } from './tagTheme';
6+
7+
type TagAriaAttributes = Pick<
8+
React.AriaAttributes,
9+
'aria-label' | 'aria-describedby' | 'aria-disabled' | 'aria-labelledby'
10+
>;
11+
12+
export type TagLabelType = Omit<IText<string>, 'children'> & {
13+
content?: string;
14+
};
15+
16+
/**
17+
* @description
18+
* Tag props
19+
*/
20+
export interface ITagStandAlone extends TagAriaAttributes {
21+
styles: TagPropsStylesType;
22+
icon?: IElementOrIcon;
23+
label?: TagLabelType;
24+
dataTestId?: string;
25+
}
26+
27+
/**
28+
* @description
29+
* Tag props
30+
* @interface ITag
31+
*/
32+
export interface ITag<
33+
V = undefined extends string | unknown ? string | undefined : string | unknown,
34+
> extends Omit<ITagStandAlone, 'styles'>,
35+
Omit<CustomTokenTypes<TagPropsStylesType>, 'cts' | 'extraCt'> {
36+
variant: V;
37+
}

0 commit comments

Comments
 (0)