Skip to content

Commit 8d0c57e

Browse files
authored
Feat/tet 849/card (#128)
* feat(card): TET-849 add card component with a story * test: TET-849 write tests for card component * feat: TET-849 replace dot notation with a single component * feat: TET-849 add border, fix HeaderAvatar story * feat: TET-849 specify avatar props * feat: TET-849 fix Card Props examples in tests and stories
1 parent b407d25 commit 8d0c57e

File tree

14 files changed

+801
-35
lines changed

14 files changed

+801
-35
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@ module.exports = {
55
],
66
ignorePatterns: ['examples/**/*'],
77
rules: {
8-
'@typescript-eslint/no-use-before-define': [
9-
2,
10-
{
11-
functions: false,
12-
typedefs: false,
13-
},
14-
],
8+
'@typescript-eslint/no-use-before-define': 'off',
159
'react/prop-types': 'off',
1610
},
1711
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"@virtuslab/tetrisly-icons": "^1.1.1",
44+
"nanoid": "^5.0.6",
4445
"react-is": "^18.2.0",
4546
"rfdc": "^1.3.0"
4647
},

src/components/Card/Card.props.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { ReactNode } from 'react';
2+
3+
import type { CardConfig } from './Card.styles';
4+
import type { CardFooterProps } from './CardFooter/CardFooter.props';
5+
import type { CardHeaderProps } from './CardHeader/CardHeader.props';
6+
7+
import { MarginProps } from '@/types';
8+
9+
export type CardProps = {
10+
custom?: CardConfig;
11+
children: ReactNode;
12+
header?: Omit<CardHeaderProps, 'styles'>;
13+
footer?: Omit<CardFooterProps, 'styles'>;
14+
} & MarginProps;

src/components/Card/Card.stories.tsx

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
3+
import { Card } from './Card';
4+
import { Badge } from '../Badge';
5+
6+
import { TetDocs } from '@/docs-components/TetDocs';
7+
import { tet } from '@/tetrisly';
8+
9+
const meta = {
10+
title: 'Card',
11+
component: Card,
12+
tags: ['autodocs'],
13+
argTypes: {},
14+
args: {
15+
children: (
16+
<tet.div
17+
display="flex"
18+
flexDirection="column"
19+
gap="$space-component-gap-2xLarge"
20+
minWidth="432px"
21+
>
22+
<Badge appearance="blue" emphasis="medium" label="In Progress" />
23+
<tet.div display="flex" flexDirection="column">
24+
<tet.span color="$color-content-secondary" text="$typo-body-small">
25+
Task:
26+
</tet.span>
27+
<tet.span color="$color-content-primary" text="$typo-body-large">
28+
Creating React components
29+
</tet.span>
30+
</tet.div>
31+
<tet.div display="flex" gap="$space-component-gap-2xLarge">
32+
<tet.div display="flex" flexDirection="column" flexGrow={1}>
33+
<tet.span color="$color-content-secondary" text="$typo-body-small">
34+
Created
35+
</tet.span>
36+
<tet.span color="$color-content-primary" text="$typo-body-medium">
37+
Mon, 14 Feb 2023
38+
</tet.span>
39+
</tet.div>
40+
<tet.div display="flex" flexDirection="column" flexGrow={1}>
41+
<tet.span color="$color-content-secondary" text="$typo-body-small">
42+
Last modified
43+
</tet.span>
44+
<tet.span color="$color-content-primary" text="$typo-body-medium">
45+
Today, 5:23 am
46+
</tet.span>
47+
</tet.div>
48+
</tet.div>
49+
</tet.div>
50+
),
51+
},
52+
parameters: {
53+
docs: {
54+
description: {
55+
component:
56+
'A Modal that displays content and actions in a visually organized manner. Cards are typically used to present grouped information, like products, articles, or user profiles, within a grid or list.',
57+
},
58+
page: () => (
59+
<TetDocs docs="https://docs.tetrisly.com/components/in-progress/card" />
60+
),
61+
},
62+
},
63+
} satisfies Meta<typeof Card>;
64+
65+
export default meta;
66+
type Story = StoryObj<typeof meta>;
67+
68+
export const Default: Story = {
69+
args: {
70+
header: {
71+
title: 'Card Title',
72+
description: 'Card Description',
73+
beforeComponent: {
74+
icon: { name: '20-wallet' },
75+
},
76+
actions: [
77+
{ label: 'Add', onClick: () => {}, beforeIcon: '20-plus' },
78+
{ label: 'Cancel', onClick: () => {} },
79+
],
80+
},
81+
82+
footer: {
83+
actions: [
84+
{ label: 'Details', appearance: 'secondary' },
85+
{ label: 'Mark as resolved', appearance: 'primary' },
86+
],
87+
},
88+
},
89+
};
90+
91+
export const Content: Story = {};
92+
export const Header: Story = {
93+
args: {
94+
header: {
95+
title: 'Card Title',
96+
},
97+
},
98+
};
99+
export const HeaderDescription: Story = {
100+
args: {
101+
header: { title: 'Card Title', description: 'Card Description' },
102+
},
103+
};
104+
105+
export const HeaderIcon: Story = {
106+
args: {
107+
header: {
108+
title: 'Card Title',
109+
beforeComponent: {
110+
icon: { name: '20-wallet' },
111+
},
112+
},
113+
},
114+
};
115+
116+
export const HeaderAvatar: Story = {
117+
args: {
118+
header: {
119+
title: 'Card Title',
120+
beforeComponent: {
121+
avatar: {
122+
image: 'https://thispersondoesnotexist.com/',
123+
},
124+
},
125+
},
126+
},
127+
};
128+
129+
export const HeaderActions: Story = {
130+
args: {
131+
header: {
132+
title: 'Card Title',
133+
actions: [
134+
{ label: 'Add', onClick: () => {}, beforeIcon: '20-plus' },
135+
{ label: 'Cancel', onClick: () => {} },
136+
],
137+
},
138+
},
139+
};
140+
141+
export const Footer: Story = {
142+
args: {
143+
footer: {
144+
actions: [
145+
{ label: 'Add', onClick: () => {}, beforeIcon: '20-plus' },
146+
{ label: 'Cancel', onClick: () => {} },
147+
],
148+
},
149+
},
150+
};

src/components/Card/Card.styles.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type { BaseProps } from '@/types/BaseProps';
2+
3+
export type CardHeaderConfig = {
4+
title?: BaseProps;
5+
content?: BaseProps;
6+
description?: BaseProps;
7+
beforeComponent?: BaseProps;
8+
actions?: BaseProps;
9+
} & BaseProps;
10+
11+
export type CardContentConfig = BaseProps;
12+
13+
export type CardFooterConfig = {
14+
actions?: BaseProps;
15+
} & BaseProps;
16+
17+
export type CardConfig = BaseProps & {
18+
innerElements?: {
19+
header?: CardHeaderConfig;
20+
content?: CardContentConfig;
21+
footer?: CardFooterConfig;
22+
};
23+
};
24+
25+
export const cardConfig = {
26+
display: 'flex',
27+
w: 'fit-content',
28+
p: '$space-component-padding-null',
29+
flexDirection: 'column',
30+
alignItems: 'flex-start',
31+
gap: '$space-component-padding-null',
32+
borderRadius: '$border-radius-xLarge',
33+
bg: '$color-background-default',
34+
boxShadow: '$elevation-bottom-200',
35+
borderWidth: '$border-width-small',
36+
borderStyle: '$border-style-solid',
37+
borderColor: '$color-border-defaultA',
38+
overflow: 'hidden',
39+
innerElements: {
40+
header: {
41+
display: 'flex',
42+
w: '100%',
43+
py: '$space-component-padding-large',
44+
px: '$space-component-padding-2xLarge',
45+
alignItems: 'center',
46+
gap: '$space-component-gap-large',
47+
borderBottomWidth: '$border-width-small',
48+
borderStyle: '$border-style-solid',
49+
borderColor: '$color-border-defaultA',
50+
beforeComponent: {
51+
display: 'flex',
52+
padding: '$space-component-padding-null',
53+
alignItems: 'center',
54+
gap: '$space-component-gap-null',
55+
},
56+
content: {
57+
display: 'flex',
58+
padding: '$space-component-padding-null',
59+
flexDirection: 'column',
60+
alignItems: 'flex-start',
61+
gap: '$space-component-padding-null',
62+
flex: '1 0 0',
63+
},
64+
title: {
65+
color: '$color-content-primary',
66+
text: '$typo-body-strong-large',
67+
},
68+
description: {
69+
color: '$color-content-secondary',
70+
text: '$typo-body-medium',
71+
},
72+
actions: {
73+
display: 'flex',
74+
padding: '$space-component-padding-null',
75+
justifyContent: 'flex-end',
76+
alignItems: 'center',
77+
gap: '$space-component-gap-large',
78+
},
79+
},
80+
content: { padding: '$space-component-padding-2xLarge', w: '100%' },
81+
footer: {
82+
display: 'flex',
83+
px: '$space-component-padding-medium',
84+
pb: '$space-component-padding-medium',
85+
w: '100%',
86+
actions: {
87+
w: '100%',
88+
display: 'flex',
89+
padding: '$space-component-padding-null',
90+
justifyContent: 'flex-end',
91+
alignItems: 'center',
92+
gap: '$space-component-gap-small',
93+
},
94+
},
95+
},
96+
} as const satisfies CardConfig;

0 commit comments

Comments
 (0)