@stackshift-ui/card 6.0.11-beta.2 → 7.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -7
- package/src/card.test.tsx +321 -6
- package/src/card.tsx +75 -30
- package/src/setupTests.ts +4 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackshift-ui/card",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "7.0.0-beta.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"author": "WebriQ <info@webriq.com>",
|
|
15
15
|
"devDependencies": {
|
|
16
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
16
17
|
"@testing-library/react": "^16.0.1",
|
|
17
18
|
"@types/node": "^22.7.0",
|
|
18
19
|
"@types/react": "^18.3.9",
|
|
@@ -29,20 +30,20 @@
|
|
|
29
30
|
"typescript": "^5.6.2",
|
|
30
31
|
"vite-tsconfig-paths": "^5.0.1",
|
|
31
32
|
"vitest": "^2.1.1",
|
|
32
|
-
"@stackshift-ui/eslint-config": "6.0.10
|
|
33
|
-
"@stackshift-ui/typescript-config": "6.0.10
|
|
33
|
+
"@stackshift-ui/eslint-config": "6.0.10",
|
|
34
|
+
"@stackshift-ui/typescript-config": "6.0.10"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"classnames": "^2.5.1",
|
|
37
|
-
"@stackshift-ui/scripts": "6.0
|
|
38
|
-
"@stackshift-ui/system": "6.0
|
|
38
|
+
"@stackshift-ui/scripts": "6.1.0-beta.0",
|
|
39
|
+
"@stackshift-ui/system": "6.1.0-beta.0"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
42
|
+
"@stackshift-ui/system": ">=6.1.0-beta.0",
|
|
41
43
|
"@types/react": "16.8 - 19",
|
|
42
44
|
"next": "10 - 14",
|
|
43
45
|
"react": "16.8 - 19",
|
|
44
|
-
"react-dom": "16.8 - 19"
|
|
45
|
-
"@stackshift-ui/system": ">=6.0.11-beta.2"
|
|
46
|
+
"react-dom": "16.8 - 19"
|
|
46
47
|
},
|
|
47
48
|
"peerDependenciesMeta": {
|
|
48
49
|
"next": {
|
package/src/card.test.tsx
CHANGED
|
@@ -1,13 +1,328 @@
|
|
|
1
1
|
import { cleanup, render, screen } from "@testing-library/react";
|
|
2
2
|
import { afterEach, describe, test } from "vitest";
|
|
3
|
-
import { Card } from "./card";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card";
|
|
4
4
|
|
|
5
|
-
describe.concurrent("
|
|
5
|
+
describe.concurrent("Card Components", () => {
|
|
6
6
|
afterEach(cleanup);
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
describe("Card", () => {
|
|
9
|
+
afterEach(cleanup);
|
|
10
|
+
|
|
11
|
+
test("Common: Card - test if renders without errors", ({ expect }) => {
|
|
12
|
+
const { unmount } = render(<Card data-testid="card-1" />);
|
|
13
|
+
expect(screen.getByTestId("card-1")).toBeInTheDocument();
|
|
14
|
+
unmount();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("Common: Card - test if renders with custom className", ({ expect }) => {
|
|
18
|
+
const customClass = "my-custom-class";
|
|
19
|
+
const { unmount } = render(
|
|
20
|
+
<Card data-testid="card-with-custom-class" className={customClass} />,
|
|
21
|
+
);
|
|
22
|
+
expect(screen.getByTestId("card-with-custom-class")).toHaveClass(customClass);
|
|
23
|
+
unmount();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("applies default classes", ({ expect }) => {
|
|
27
|
+
const { unmount } = render(<Card data-testid="card-with-default-classes" />);
|
|
28
|
+
const card = screen.getByTestId("card-with-default-classes");
|
|
29
|
+
expect(card).toHaveClass(
|
|
30
|
+
"rounded-lg",
|
|
31
|
+
"border",
|
|
32
|
+
"bg-card",
|
|
33
|
+
"text-card-foreground",
|
|
34
|
+
"shadow-sm",
|
|
35
|
+
);
|
|
36
|
+
unmount();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("Common: Card - test if renders with custom id and role", ({ expect }) => {
|
|
40
|
+
const { unmount } = render(
|
|
41
|
+
<Card data-testid="card-with-id-and-role" id="test-id" role="region" />,
|
|
42
|
+
);
|
|
43
|
+
const card = screen.getByTestId("card-with-id-and-role");
|
|
44
|
+
expect(card).toHaveAttribute("id", "test-id");
|
|
45
|
+
expect(card).toHaveAttribute("role", "region");
|
|
46
|
+
unmount();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("Common: Card - test if renders with custom content", ({ expect }) => {
|
|
50
|
+
const { unmount } = render(
|
|
51
|
+
<Card data-testid="card-with-content">
|
|
52
|
+
<span>Card With custom content</span>
|
|
53
|
+
</Card>,
|
|
54
|
+
);
|
|
55
|
+
expect(screen.getByText("Card With custom content")).toBeInTheDocument();
|
|
56
|
+
unmount();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("CardHeader", () => {
|
|
61
|
+
afterEach(cleanup);
|
|
62
|
+
|
|
63
|
+
test("Common: CardHeader - test if renders without errors", ({ expect }) => {
|
|
64
|
+
const { unmount } = render(<CardHeader data-testid="card-header" />);
|
|
65
|
+
expect(screen.getByTestId("card-header")).toBeInTheDocument();
|
|
66
|
+
unmount();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("Common: CardHeader - test default classes", ({ expect }) => {
|
|
70
|
+
const { unmount } = render(<CardHeader data-testid="card-header-with-default-classes" />);
|
|
71
|
+
const header = screen.getByTestId("card-header-with-default-classes");
|
|
72
|
+
expect(header).toHaveClass("flex", "flex-col", "space-y-1.5", "p-6");
|
|
73
|
+
unmount();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("Common: CardHeader - test if renders with custom className", ({ expect }) => {
|
|
77
|
+
const customClass = "custom-header-class";
|
|
78
|
+
const { unmount } = render(
|
|
79
|
+
<CardHeader data-testid="card-header-with-custom-class" className={customClass} />,
|
|
80
|
+
);
|
|
81
|
+
expect(screen.getByTestId("card-header-with-custom-class")).toHaveClass(customClass);
|
|
82
|
+
unmount();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("CardTitle", () => {
|
|
87
|
+
afterEach(cleanup);
|
|
88
|
+
|
|
89
|
+
test("Common: CardTitle - test if renders without errors", ({ expect }) => {
|
|
90
|
+
const { unmount } = render(<CardTitle data-testid="card-title" />);
|
|
91
|
+
expect(screen.getByTestId("card-title")).toBeInTheDocument();
|
|
92
|
+
unmount();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("Common: CardTitle - test default classes", ({ expect }) => {
|
|
96
|
+
const { unmount } = render(<CardTitle data-testid="card-title-with-default-classes" />);
|
|
97
|
+
const title = screen.getByTestId("card-title-with-default-classes");
|
|
98
|
+
expect(title).toHaveClass("text-2xl", "font-semibold", "leading-none", "tracking-tight");
|
|
99
|
+
unmount();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("Common: CardTitle - test if renders with custom className", ({ expect }) => {
|
|
103
|
+
const customClass = "custom-title-class";
|
|
104
|
+
const { unmount } = render(
|
|
105
|
+
<CardTitle data-testid="card-title-with-custom-class" className={customClass} />,
|
|
106
|
+
);
|
|
107
|
+
expect(screen.getByTestId("card-title-with-custom-class")).toHaveClass(customClass);
|
|
108
|
+
unmount();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("Common: CardTitle - test if renders with custom content", ({ expect }) => {
|
|
112
|
+
const { unmount } = render(
|
|
113
|
+
<CardTitle data-testid="card-title-with-content">Card Title With Content</CardTitle>,
|
|
114
|
+
);
|
|
115
|
+
expect(screen.getByText("Card Title With Content")).toBeInTheDocument();
|
|
116
|
+
unmount();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("CardDescription", () => {
|
|
121
|
+
afterEach(cleanup);
|
|
122
|
+
|
|
123
|
+
test("Common: CardDescription - test if renders without errors", ({ expect }) => {
|
|
124
|
+
const { unmount } = render(<CardDescription data-testid="card-description" />);
|
|
125
|
+
expect(screen.getByTestId("card-description")).toBeInTheDocument();
|
|
126
|
+
unmount();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("Common: CardDescription - test default classes", ({ expect }) => {
|
|
130
|
+
const { unmount } = render(
|
|
131
|
+
<CardDescription data-testid="card-description-with-default-classes" />,
|
|
132
|
+
);
|
|
133
|
+
const description = screen.getByTestId("card-description-with-default-classes");
|
|
134
|
+
expect(description.getAttribute("class")).toContain("text-sm text-muted-foreground");
|
|
135
|
+
unmount();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("Common: CardDescription - test if renders with custom className", ({ expect }) => {
|
|
139
|
+
const customClass = "custom-description-class";
|
|
140
|
+
const { unmount } = render(
|
|
141
|
+
<CardDescription
|
|
142
|
+
data-testid="card-description-with-custom-class"
|
|
143
|
+
className={customClass}
|
|
144
|
+
/>,
|
|
145
|
+
);
|
|
146
|
+
expect(
|
|
147
|
+
screen.getByTestId("card-description-with-custom-class").getAttribute("class"),
|
|
148
|
+
).toContain(customClass);
|
|
149
|
+
unmount();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("Common: CardDescription - test if renders with custom content", ({ expect }) => {
|
|
153
|
+
const { unmount } = render(
|
|
154
|
+
<CardDescription data-testid="card-description">Test Description</CardDescription>,
|
|
155
|
+
);
|
|
156
|
+
expect(screen.getByText("Test Description")).toBeInTheDocument();
|
|
157
|
+
unmount();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("CardContent", () => {
|
|
162
|
+
afterEach(cleanup);
|
|
163
|
+
|
|
164
|
+
test("Common: CardContent - test if renders without errors", ({ expect }) => {
|
|
165
|
+
const { unmount } = render(<CardContent data-testid="card-content" />);
|
|
166
|
+
expect(screen.getByTestId("card-content")).toBeInTheDocument();
|
|
167
|
+
unmount();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("Common: CardContent - test default classes", ({ expect }) => {
|
|
171
|
+
const { unmount } = render(<CardContent data-testid="card-content-with-default-classes" />);
|
|
172
|
+
const content = screen.getByTestId("card-content-with-default-classes");
|
|
173
|
+
expect(content.getAttribute("class")).toContain("p-6 pt-0");
|
|
174
|
+
unmount();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("Common: CardContent - test if renders with custom className", ({ expect }) => {
|
|
178
|
+
const customClass = "custom-content-class";
|
|
179
|
+
const { unmount } = render(
|
|
180
|
+
<CardContent data-testid="card-content-with-custom-class" className={customClass} />,
|
|
181
|
+
);
|
|
182
|
+
expect(screen.getByTestId("card-content-with-custom-class").getAttribute("class")).toContain(
|
|
183
|
+
customClass,
|
|
184
|
+
);
|
|
185
|
+
unmount();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("Common: CardContent - test if renders with custom content", ({ expect }) => {
|
|
189
|
+
const { unmount } = render(
|
|
190
|
+
<CardContent data-testid="card-content">Test Content</CardContent>,
|
|
191
|
+
);
|
|
192
|
+
expect(screen.getByText("Test Content")).toBeInTheDocument();
|
|
193
|
+
unmount();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("CardFooter", () => {
|
|
198
|
+
afterEach(cleanup);
|
|
199
|
+
|
|
200
|
+
test("Common: CardFooter - test if renders without errors", ({ expect }) => {
|
|
201
|
+
const { unmount } = render(<CardFooter data-testid="card-footer" />);
|
|
202
|
+
expect(screen.getByTestId("card-footer")).toBeInTheDocument();
|
|
203
|
+
unmount();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("Common: CardFooter - test default classes", ({ expect }) => {
|
|
207
|
+
const { unmount } = render(<CardFooter data-testid="card-footer-with-default-classes" />);
|
|
208
|
+
const footer = screen.getByTestId("card-footer-with-default-classes");
|
|
209
|
+
expect(footer.getAttribute("class")).toContain("flex items-center p-6 pt-0");
|
|
210
|
+
unmount();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("Common: CardFooter - test if renders with custom className", ({ expect }) => {
|
|
214
|
+
const customClass = "custom-footer-class";
|
|
215
|
+
const { unmount } = render(
|
|
216
|
+
<CardFooter data-testid="card-footer-with-custom-class" className={customClass} />,
|
|
217
|
+
);
|
|
218
|
+
expect(screen.getByTestId("card-footer-with-custom-class").getAttribute("class")).toContain(
|
|
219
|
+
customClass,
|
|
220
|
+
);
|
|
221
|
+
unmount();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("Common: CardFooter - test if renders with custom content", ({ expect }) => {
|
|
225
|
+
const { unmount } = render(<CardFooter data-testid="card-footer">Footer Content</CardFooter>);
|
|
226
|
+
expect(screen.getByText("Footer Content")).toBeInTheDocument();
|
|
227
|
+
unmount();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("Card Integration", () => {
|
|
232
|
+
afterEach(cleanup);
|
|
233
|
+
|
|
234
|
+
test("Common: Card - test if renders with all components", ({ expect }) => {
|
|
235
|
+
const { unmount } = render(
|
|
236
|
+
<Card data-testid="card-with-all-components">
|
|
237
|
+
<CardHeader data-testid="card-header-with-all-components">
|
|
238
|
+
<CardTitle data-testid="card-title-with-all-components">Card Title</CardTitle>
|
|
239
|
+
<CardDescription data-testid="card-description-with-all-components">
|
|
240
|
+
Card Description
|
|
241
|
+
</CardDescription>
|
|
242
|
+
</CardHeader>
|
|
243
|
+
<CardContent data-testid="card-content-with-all-components">
|
|
244
|
+
<p>This is the card content.</p>
|
|
245
|
+
</CardContent>
|
|
246
|
+
<CardFooter data-testid="card-footer-with-all-components">
|
|
247
|
+
<button>Action Button</button>
|
|
248
|
+
</CardFooter>
|
|
249
|
+
</Card>,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Verify all components are rendered
|
|
253
|
+
expect(screen.getByTestId("card-with-all-components")).toBeInTheDocument();
|
|
254
|
+
expect(screen.getByTestId("card-header-with-all-components")).toBeInTheDocument();
|
|
255
|
+
expect(screen.getByTestId("card-title-with-all-components")).toBeInTheDocument();
|
|
256
|
+
expect(screen.getByTestId("card-description-with-all-components")).toBeInTheDocument();
|
|
257
|
+
expect(screen.getByTestId("card-content-with-all-components")).toBeInTheDocument();
|
|
258
|
+
expect(screen.getByTestId("card-footer-with-all-components")).toBeInTheDocument();
|
|
259
|
+
|
|
260
|
+
// Verify content is rendered correctly
|
|
261
|
+
expect(screen.getByText("Card Title")).toBeInTheDocument();
|
|
262
|
+
expect(screen.getByText("Card Description")).toBeInTheDocument();
|
|
263
|
+
expect(screen.getByText("This is the card content.")).toBeInTheDocument();
|
|
264
|
+
expect(screen.getByText("Action Button")).toBeInTheDocument();
|
|
265
|
+
unmount();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("Common: Card - test if components are rendered in correct order", ({ expect }) => {
|
|
269
|
+
const { unmount } = render(
|
|
270
|
+
<Card data-testid="card-content-order">
|
|
271
|
+
<CardHeader data-testid="card-header-content-order">
|
|
272
|
+
<CardTitle data-testid="card-title-content-order">Title</CardTitle>
|
|
273
|
+
</CardHeader>
|
|
274
|
+
<CardContent data-testid="card-content-content-order">Content</CardContent>
|
|
275
|
+
</Card>,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const card = screen.getByTestId("card-content-order");
|
|
279
|
+
const header = screen.getByTestId("card-header-content-order");
|
|
280
|
+
const title = screen.getByTestId("card-title-content-order");
|
|
281
|
+
const content = screen.getByTestId("card-content-content-order");
|
|
282
|
+
|
|
283
|
+
// Verify hierarchy
|
|
284
|
+
expect(card).toContainElement(header);
|
|
285
|
+
expect(card).toContainElement(content);
|
|
286
|
+
expect(header).toContainElement(title);
|
|
287
|
+
unmount();
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("Accessibility", () => {
|
|
292
|
+
afterEach(cleanup);
|
|
293
|
+
|
|
294
|
+
test("Common: Card - test if renders with correct role", ({ expect }) => {
|
|
295
|
+
const { unmount } = render(
|
|
296
|
+
<Card data-testid="card" role="article">
|
|
297
|
+
<CardHeader data-testid="card-header">
|
|
298
|
+
<CardTitle data-testid="card-title">Accessible Title</CardTitle>
|
|
299
|
+
<CardDescription data-testid="card-description">Accessible Description</CardDescription>
|
|
300
|
+
</CardHeader>
|
|
301
|
+
<CardContent data-testid="card-content">Accessible Content</CardContent>
|
|
302
|
+
<CardFooter data-testid="card-footer">Accessible Footer</CardFooter>
|
|
303
|
+
</Card>,
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(screen.getByRole("article")).toBeInTheDocument();
|
|
307
|
+
expect(screen.getByText("Accessible Title")).toBeInTheDocument();
|
|
308
|
+
expect(screen.getByText("Accessible Description")).toBeInTheDocument();
|
|
309
|
+
unmount();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("Common: Card - test if renders with ARIA attributes", ({ expect }) => {
|
|
313
|
+
const { unmount } = render(
|
|
314
|
+
<Card
|
|
315
|
+
data-testid="card-with-aria"
|
|
316
|
+
aria-label="Test card"
|
|
317
|
+
aria-describedby="card-description">
|
|
318
|
+
<CardContent id="card-description">Card with ARIA attributes</CardContent>
|
|
319
|
+
</Card>,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const card = screen.getByTestId("card-with-aria");
|
|
323
|
+
expect(card).toHaveAttribute("aria-label", "Test card");
|
|
324
|
+
expect(card).toHaveAttribute("aria-describedby", "card-description");
|
|
325
|
+
unmount();
|
|
326
|
+
});
|
|
12
327
|
});
|
|
13
328
|
});
|
package/src/card.tsx
CHANGED
|
@@ -1,38 +1,83 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { ElementType, HTMLProps, ReactNode } from "react";
|
|
3
|
-
import cn from "classnames";
|
|
1
|
+
import * as React from "react";
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export interface CardProps extends Omit<HTMLProps<HTMLElement>, "as"> {
|
|
8
|
-
borderRadius?: CardBorderRadius;
|
|
9
|
-
children?: ReactNode;
|
|
10
|
-
className?: string;
|
|
11
|
-
as?: ElementType;
|
|
12
|
-
}
|
|
3
|
+
import { cn, DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
|
|
13
4
|
|
|
14
5
|
const displayName = "Card";
|
|
6
|
+
const displayNameHeader = "CardHeader";
|
|
7
|
+
const displayNameTitle = "CardTitle";
|
|
8
|
+
const displayNameDescription = "CardDescription";
|
|
9
|
+
const displayNameContent = "CardContent";
|
|
10
|
+
const displayNameFooter = "CardFooter";
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
13
|
+
({ className, ...props }, ref) => {
|
|
14
|
+
const { [displayName]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
16
|
+
return (
|
|
17
|
+
<Component
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
Card.displayName = displayName;
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
28
|
+
({ className, ...props }, ref) => {
|
|
29
|
+
const { [displayNameHeader]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
return (
|
|
32
|
+
<Component ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
CardHeader.displayName = displayNameHeader;
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
39
|
+
({ className, ...props }, ref) => {
|
|
40
|
+
const { [displayNameTitle]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
41
|
+
return (
|
|
42
|
+
<Component
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
CardTitle.displayName = displayNameTitle;
|
|
51
|
+
|
|
52
|
+
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
53
|
+
({ className, ...props }, ref) => {
|
|
54
|
+
const { [displayNameDescription]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Component ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
CardDescription.displayName = displayNameDescription;
|
|
62
|
+
|
|
63
|
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
64
|
+
({ className, ...props }, ref) => {
|
|
65
|
+
const { [displayNameContent]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
66
|
+
|
|
67
|
+
return <Component ref={ref} className={cn("p-6 pt-0", className)} {...props} />;
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
CardContent.displayName = displayNameContent;
|
|
71
|
+
|
|
72
|
+
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
73
|
+
({ className, ...props }, ref) => {
|
|
74
|
+
const { [displayNameFooter]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Component ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
CardFooter.displayName = displayNameFooter;
|
|
82
|
+
|
|
83
|
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|