@sproutsocial/seeds-react-card 1.1.53 → 1.1.55
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/dist/card.css +152 -0
- package/dist/esm/index.js +298 -74
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +38 -6
- package/dist/index.d.ts +38 -6
- package/dist/index.js +293 -77
- package/dist/index.js.map +1 -1
- package/package.json +23 -12
- package/.eslintignore +0 -6
- package/.eslintrc.js +0 -4
- package/.turbo/turbo-build.log +0 -21
- package/CHANGELOG.md +0 -399
- package/jest.config.js +0 -9
- package/src/Card.feature +0 -122
- package/src/Card.stories.tsx +0 -389
- package/src/Card.tsx +0 -87
- package/src/CardTypes.ts +0 -133
- package/src/__tests__/Card.test.tsx +0 -313
- package/src/__tests__/CardTypes.typetest.tsx +0 -78
- package/src/index.ts +0 -6
- package/src/styles.tsx +0 -176
- package/src/subComponents.tsx +0 -110
- package/src/utils.ts +0 -61
- package/tsconfig.json +0 -15
- package/tsup.config.ts +0 -12
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
render,
|
|
4
|
-
screen,
|
|
5
|
-
waitFor,
|
|
6
|
-
} from "@sproutsocial/seeds-react-testing-library";
|
|
7
|
-
import { PointerEventsCheckLevel } from "@testing-library/user-event";
|
|
8
|
-
import Card from "../";
|
|
9
|
-
import Badge from "@sproutsocial/seeds-react-badge";
|
|
10
|
-
import {
|
|
11
|
-
CardContent,
|
|
12
|
-
CardFooter,
|
|
13
|
-
CardHeader,
|
|
14
|
-
CardLink,
|
|
15
|
-
} from "../subComponents";
|
|
16
|
-
import { theme } from "@sproutsocial/seeds-react-theme";
|
|
17
|
-
|
|
18
|
-
jest.mock("../utils");
|
|
19
|
-
const mockCardClick = jest.fn();
|
|
20
|
-
|
|
21
|
-
describe("A card is interactive", () => {
|
|
22
|
-
it("should be clickable", async () => {
|
|
23
|
-
const { user } = render(
|
|
24
|
-
<Card role="button" onClick={mockCardClick}>
|
|
25
|
-
Test
|
|
26
|
-
</Card>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const card = screen.getByRole("button");
|
|
30
|
-
|
|
31
|
-
await user.click(card);
|
|
32
|
-
expect(mockCardClick).toBeCalledTimes(1);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should function as a link", async () => {
|
|
36
|
-
const { user } = render(
|
|
37
|
-
<Card role="link" href="https://sproutsocial.com/">
|
|
38
|
-
Hello
|
|
39
|
-
<CardLink>Test</CardLink>
|
|
40
|
-
</Card>
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const card = screen.getByText("Hello");
|
|
44
|
-
const link = screen.getByText("Test");
|
|
45
|
-
|
|
46
|
-
// listen to the child link to make sure that clicking the parent Card clicks the link programmatically
|
|
47
|
-
link.addEventListener("click", mockCardClick);
|
|
48
|
-
|
|
49
|
-
expect(card).toContainElement(link);
|
|
50
|
-
expect(link).toHaveAttribute("href", "https://sproutsocial.com/");
|
|
51
|
-
|
|
52
|
-
await user.click(card);
|
|
53
|
-
expect(mockCardClick).toBeCalled();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("should function as a button", async () => {
|
|
57
|
-
const { user } = render(
|
|
58
|
-
<Card role="button" onClick={mockCardClick}>
|
|
59
|
-
Test
|
|
60
|
-
</Card>
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
const card = screen.getByRole("button");
|
|
64
|
-
expect(card).toHaveAttribute("role", "button");
|
|
65
|
-
|
|
66
|
-
await user.click(card);
|
|
67
|
-
expect(mockCardClick).toBeCalledTimes(1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("can be purely presentational", () => {
|
|
71
|
-
render(<Card role="presentation">Test</Card>);
|
|
72
|
-
|
|
73
|
-
const card = screen.getByRole("presentation");
|
|
74
|
-
expect(card).toHaveAttribute("role", "presentation");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("can be disabled", async () => {
|
|
78
|
-
const { user } = render(
|
|
79
|
-
<Card role="button" onClick={mockCardClick} disabled={true}>
|
|
80
|
-
Test
|
|
81
|
-
</Card>
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const card = screen.getByRole("button");
|
|
85
|
-
|
|
86
|
-
// Disabled cards should have aria-disabled attribute
|
|
87
|
-
expect(card).toHaveAttribute("aria-disabled", "true");
|
|
88
|
-
|
|
89
|
-
// Disabled cards should not be focusable
|
|
90
|
-
expect(card).toHaveAttribute("tabindex", "-1");
|
|
91
|
-
|
|
92
|
-
// Disabled cards should not respond to clicks
|
|
93
|
-
await user
|
|
94
|
-
.setup({
|
|
95
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
|
96
|
-
})
|
|
97
|
-
.click(card);
|
|
98
|
-
expect(mockCardClick).not.toHaveBeenCalled();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("disabled cards do not respond to keyboard events", async () => {
|
|
102
|
-
const { user } = render(
|
|
103
|
-
<Card role="button" onClick={mockCardClick} disabled={true}>
|
|
104
|
-
Test
|
|
105
|
-
</Card>
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const card = screen.getByRole("button");
|
|
109
|
-
|
|
110
|
-
// Try to focus the card by tabbing
|
|
111
|
-
await user.tab();
|
|
112
|
-
|
|
113
|
-
// Disabled cards should not receive focus
|
|
114
|
-
expect(card).not.toHaveFocus();
|
|
115
|
-
|
|
116
|
-
// Even if we force focus (for testing), Enter key should not trigger onClick
|
|
117
|
-
card.focus();
|
|
118
|
-
await user.keyboard("{Enter}");
|
|
119
|
-
expect(mockCardClick).not.toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("should have an adjustable hover state style for interactive cards", async () => {
|
|
123
|
-
const { user } = render(
|
|
124
|
-
<Card role="button" onClick={mockCardClick} elevation="high">
|
|
125
|
-
Test
|
|
126
|
-
</Card>
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const card = screen.getByRole("button");
|
|
130
|
-
|
|
131
|
-
// apparently, jest-dom can't do this with toHaveStyle
|
|
132
|
-
// https://github.com/testing-library/jest-dom/issues/59
|
|
133
|
-
//
|
|
134
|
-
// have to use toHaveStyleRule from jest-styled-comps
|
|
135
|
-
// https://github.com/styled-components/jest-styled-components#tohavestylerule
|
|
136
|
-
await user.hover(card);
|
|
137
|
-
expect(card).toHaveStyleRule("box-shadow", theme.shadows.high, {
|
|
138
|
-
modifier: '&[role="button"]:hover',
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("can be focused when interactive", async () => {
|
|
143
|
-
const { user } = render(
|
|
144
|
-
<Card role="button" onClick={mockCardClick}>
|
|
145
|
-
Test
|
|
146
|
-
<button>child click test</button>
|
|
147
|
-
</Card>
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const [card, button] = screen.getAllByRole("button");
|
|
151
|
-
|
|
152
|
-
expect(card).toHaveAttribute("tabindex", "0");
|
|
153
|
-
|
|
154
|
-
await user.tab();
|
|
155
|
-
expect(card).toHaveFocus();
|
|
156
|
-
|
|
157
|
-
await user.tab();
|
|
158
|
-
expect(button).toHaveFocus();
|
|
159
|
-
|
|
160
|
-
await user.tab({ shift: true });
|
|
161
|
-
expect(card).toHaveFocus();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('handles onKeyDown "enter"', async () => {
|
|
165
|
-
const { rerender, debug, user } = render(
|
|
166
|
-
<Card role="button" onClick={mockCardClick}>
|
|
167
|
-
Test
|
|
168
|
-
</Card>
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
debug;
|
|
172
|
-
|
|
173
|
-
const cardAsButton = screen.getByRole("button");
|
|
174
|
-
|
|
175
|
-
await user.tab();
|
|
176
|
-
expect(cardAsButton).toHaveFocus();
|
|
177
|
-
|
|
178
|
-
await user.type(cardAsButton, "{enter}");
|
|
179
|
-
expect(mockCardClick).toBeCalledTimes(1);
|
|
180
|
-
|
|
181
|
-
rerender(
|
|
182
|
-
<Card role="link" href="https://sproutsocial.com/">
|
|
183
|
-
Hello
|
|
184
|
-
<CardLink>Test</CardLink>
|
|
185
|
-
</Card>
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const cardAsLink = screen.getByText("Test");
|
|
189
|
-
|
|
190
|
-
await user.tab();
|
|
191
|
-
expect(cardAsLink).toHaveFocus();
|
|
192
|
-
expect(cardAsLink).toHaveAttribute("href", "https://sproutsocial.com/");
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("is selectable", async () => {
|
|
196
|
-
const TestCheckboxCard = () => {
|
|
197
|
-
const [selected, setSelected] = React.useState<boolean>(false);
|
|
198
|
-
return (
|
|
199
|
-
<Card
|
|
200
|
-
role="checkbox"
|
|
201
|
-
onClick={() => setSelected(!selected)}
|
|
202
|
-
selected={selected}
|
|
203
|
-
>
|
|
204
|
-
Test
|
|
205
|
-
</Card>
|
|
206
|
-
);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const { user } = render(<TestCheckboxCard />);
|
|
210
|
-
|
|
211
|
-
const card = screen.getByRole("checkbox");
|
|
212
|
-
await user.click(card);
|
|
213
|
-
|
|
214
|
-
await waitFor(() => expect(screen.getByRole("checkbox")).toBeChecked());
|
|
215
|
-
expect(card).toHaveStyle(
|
|
216
|
-
`border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected}`
|
|
217
|
-
);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("should support interactive children in button cards", async () => {
|
|
221
|
-
const mockChildClick = jest.fn((e) => e.stopPropagation());
|
|
222
|
-
|
|
223
|
-
const { user } = render(
|
|
224
|
-
<Card role="button" onClick={mockCardClick}>
|
|
225
|
-
Test
|
|
226
|
-
<button onClick={mockChildClick}>child click test</button>
|
|
227
|
-
</Card>
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
const buttons = screen.getAllByRole("button");
|
|
231
|
-
const card = buttons[0]!;
|
|
232
|
-
const cardChild = buttons[1]!;
|
|
233
|
-
|
|
234
|
-
// Expect clicking the card to trigger the card's onClick but not the child's
|
|
235
|
-
await user.click(card);
|
|
236
|
-
expect(mockCardClick).toBeCalledTimes(1);
|
|
237
|
-
expect(mockChildClick).toBeCalledTimes(0);
|
|
238
|
-
|
|
239
|
-
// Expect clicking the interactive child NOT to trigger the card's onClick.
|
|
240
|
-
await user.click(cardChild);
|
|
241
|
-
expect(mockCardClick).toBeCalledTimes(1);
|
|
242
|
-
expect(mockChildClick).toBeCalledTimes(1);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("presentation cards are not interactive but support interactive children", async () => {
|
|
246
|
-
const mockChildClick = jest.fn();
|
|
247
|
-
|
|
248
|
-
const { user } = render(
|
|
249
|
-
<Card role="presentation">
|
|
250
|
-
Test
|
|
251
|
-
<button onClick={mockChildClick}>child click test</button>
|
|
252
|
-
</Card>
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
const card = screen.getByRole("presentation");
|
|
256
|
-
const cardChild = screen.getByText("child click test");
|
|
257
|
-
|
|
258
|
-
// Presentation cards should not be focusable
|
|
259
|
-
expect(card).toHaveAttribute("tabindex", "-1");
|
|
260
|
-
|
|
261
|
-
// Interactive children should still work
|
|
262
|
-
await user.click(cardChild);
|
|
263
|
-
expect(mockChildClick).toBeCalledTimes(1);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
describe("A Card supports composable layouts", () => {
|
|
268
|
-
it("should support children", () => {
|
|
269
|
-
render(
|
|
270
|
-
<Card role="presentation">
|
|
271
|
-
Hello, world!
|
|
272
|
-
<Badge>Cool Badge</Badge>
|
|
273
|
-
</Card>
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
const parent = screen.getByRole("presentation");
|
|
277
|
-
const child = screen.getByText("Cool Badge");
|
|
278
|
-
|
|
279
|
-
expect(parent).toContainElement(child);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it("should accept system props", () => {
|
|
283
|
-
render(
|
|
284
|
-
<Card role="presentation" p={300}>
|
|
285
|
-
Hello, world!
|
|
286
|
-
</Card>
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
const card = screen.getByRole("presentation");
|
|
290
|
-
expect(card).toHaveStyle(`padding: ${theme.space[300]}`);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("adjusts it's styles dynamically when subcomponents are present", () => {
|
|
294
|
-
render(
|
|
295
|
-
<Card role="presentation">
|
|
296
|
-
<CardHeader>Card Header</CardHeader>
|
|
297
|
-
<CardContent>CardContent</CardContent>
|
|
298
|
-
<CardFooter>CardFooter</CardFooter>
|
|
299
|
-
</Card>
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
const card = screen.getByRole("presentation");
|
|
303
|
-
const cardSubcomponent = screen.getByText("CardFooter");
|
|
304
|
-
|
|
305
|
-
expect(cardSubcomponent).toBeInTheDocument();
|
|
306
|
-
// We have to wait for the new classes to be set once the state changes. Not sure how to do this better but a function seems to work.
|
|
307
|
-
() => expect(card).toHaveStyle("padding: 0px");
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
afterEach(() => {
|
|
312
|
-
jest.clearAllMocks();
|
|
313
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import Card from "..";
|
|
3
|
-
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
-
function CardTypes() {
|
|
6
|
-
return (
|
|
7
|
-
<>
|
|
8
|
-
<Card role="link" href="https://sproutsocial.com/">
|
|
9
|
-
This is a card.
|
|
10
|
-
</Card>
|
|
11
|
-
{/* @ts-expect-error - test that href is required with role=link */}
|
|
12
|
-
<Card role="link">This is a card.</Card>
|
|
13
|
-
{/* @ts-expect-error - test that onClick is never allowed with role=link */}
|
|
14
|
-
<Card
|
|
15
|
-
role="link"
|
|
16
|
-
onClick={() => {
|
|
17
|
-
return;
|
|
18
|
-
}}
|
|
19
|
-
>
|
|
20
|
-
This is a card.
|
|
21
|
-
</Card>
|
|
22
|
-
<Card
|
|
23
|
-
role="button"
|
|
24
|
-
onClick={() => {
|
|
25
|
-
return;
|
|
26
|
-
}}
|
|
27
|
-
>
|
|
28
|
-
This is a card.
|
|
29
|
-
</Card>
|
|
30
|
-
{/* @ts-expect-error - test that onClick is required with role=button */}
|
|
31
|
-
<Card role="button">This is a card.</Card>
|
|
32
|
-
<Card
|
|
33
|
-
role="checkbox"
|
|
34
|
-
selected={true}
|
|
35
|
-
onClick={() => {
|
|
36
|
-
return;
|
|
37
|
-
}}
|
|
38
|
-
>
|
|
39
|
-
This is a card.
|
|
40
|
-
</Card>
|
|
41
|
-
{/* @ts-expect-error - test that select is required with role=checkbox */}
|
|
42
|
-
<Card
|
|
43
|
-
role="checkbox"
|
|
44
|
-
onClick={() => {
|
|
45
|
-
return;
|
|
46
|
-
}}
|
|
47
|
-
>
|
|
48
|
-
This is a card.
|
|
49
|
-
</Card>
|
|
50
|
-
{/* @ts-expect-error - test that onClick is required with role=checkbox */}
|
|
51
|
-
<Card role="checkbox" selected={true}>
|
|
52
|
-
This is a card.
|
|
53
|
-
</Card>
|
|
54
|
-
<Card role="presentation">This is a card.</Card>
|
|
55
|
-
{/* @ts-expect-error - test that onClick is never allowed with role=presentation */}
|
|
56
|
-
<Card
|
|
57
|
-
role="presentation"
|
|
58
|
-
onClick={() => {
|
|
59
|
-
return;
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
This is a card.
|
|
63
|
-
</Card>
|
|
64
|
-
{/* @ts-expect-error - test that href is never allowed with role=presentation */}
|
|
65
|
-
<Card role="presentation" href="">
|
|
66
|
-
This is a card.
|
|
67
|
-
</Card>
|
|
68
|
-
{/* @ts-expect-error - test that selected is never allowed with role=presentation */}
|
|
69
|
-
<Card role="presentation" disabled={true} selected={true}>
|
|
70
|
-
This is a card.
|
|
71
|
-
</Card>
|
|
72
|
-
{/* @ts-expect-error - test that consumer level transient props fail */}
|
|
73
|
-
<Card role="presentation" $disabled={true} $selected={true}>
|
|
74
|
-
This is a card.
|
|
75
|
-
</Card>
|
|
76
|
-
</>
|
|
77
|
-
);
|
|
78
|
-
}
|
package/src/index.ts
DELETED
package/src/styles.tsx
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import styled from "styled-components";
|
|
2
|
-
import {
|
|
3
|
-
border,
|
|
4
|
-
color,
|
|
5
|
-
flexbox,
|
|
6
|
-
grid,
|
|
7
|
-
layout,
|
|
8
|
-
position,
|
|
9
|
-
space,
|
|
10
|
-
typography,
|
|
11
|
-
} from "styled-system";
|
|
12
|
-
import { focusRing, disabled } from "@sproutsocial/seeds-react-mixins";
|
|
13
|
-
import type {
|
|
14
|
-
TypeStyledCard,
|
|
15
|
-
TypeCardArea,
|
|
16
|
-
TypeStyledSelectedIcon,
|
|
17
|
-
TypeCardLink,
|
|
18
|
-
} from "./CardTypes";
|
|
19
|
-
import Icon from "@sproutsocial/seeds-react-icon";
|
|
20
|
-
|
|
21
|
-
// TODO: Would be really cool to cherry pick specific props from style functions. For example,
|
|
22
|
-
// removing the css prop 'color' from the color function or importing just the specific
|
|
23
|
-
// props the component needs. It appears to be possible with some and not others.
|
|
24
|
-
// https://github.com/styled-system/styled-system/issues/1569
|
|
25
|
-
|
|
26
|
-
export const StyledCardContent = styled.div<TypeCardArea>`
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
padding: ${({ theme }) => theme.space[400]};
|
|
30
|
-
box-sizing: border-box;
|
|
31
|
-
|
|
32
|
-
${border}
|
|
33
|
-
${color}
|
|
34
|
-
${flexbox}
|
|
35
|
-
${grid}
|
|
36
|
-
${layout}
|
|
37
|
-
${space}
|
|
38
|
-
`;
|
|
39
|
-
|
|
40
|
-
export const StyledCardHeader = styled(StyledCardContent)`
|
|
41
|
-
flex-direction: row;
|
|
42
|
-
border-bottom: ${({ theme }) => `${theme.borderWidths[500]} solid
|
|
43
|
-
${theme.colors.container.border.base}`};
|
|
44
|
-
border-top-left-radius: ${({ theme }) => theme.radii.inner};
|
|
45
|
-
border-top-right-radius: ${({ theme }) => theme.radii.inner};
|
|
46
|
-
|
|
47
|
-
${border}
|
|
48
|
-
${color}
|
|
49
|
-
${flexbox}
|
|
50
|
-
${grid}
|
|
51
|
-
${layout}
|
|
52
|
-
${space}
|
|
53
|
-
`;
|
|
54
|
-
|
|
55
|
-
export const StyledCardFooter = styled(StyledCardContent)`
|
|
56
|
-
flex-direction: row;
|
|
57
|
-
border-top: ${({ theme }) => `${theme.borderWidths[500]} solid
|
|
58
|
-
${theme.colors.container.border.base}`};
|
|
59
|
-
border-bottom-left-radius: ${({ theme }) => theme.radii.inner};
|
|
60
|
-
border-bottom-right-radius: ${({ theme }) => theme.radii.inner};
|
|
61
|
-
|
|
62
|
-
${border}
|
|
63
|
-
${color}
|
|
64
|
-
${flexbox}
|
|
65
|
-
${grid}
|
|
66
|
-
${layout}
|
|
67
|
-
${space}
|
|
68
|
-
`;
|
|
69
|
-
|
|
70
|
-
export const SelectedIconWrapper = styled.div`
|
|
71
|
-
display: flex;
|
|
72
|
-
align-items: center;
|
|
73
|
-
justify-content: center;
|
|
74
|
-
position: absolute;
|
|
75
|
-
top: -8px;
|
|
76
|
-
right: -8px;
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
export const StyledSelectedIcon = styled(Icon)<TypeStyledSelectedIcon>`
|
|
80
|
-
border-radius: 50%;
|
|
81
|
-
background: ${({ theme }) => theme.colors.container.background.base};
|
|
82
|
-
opacity: 0;
|
|
83
|
-
transition: opacity ${({ theme }) => theme.duration.medium};
|
|
84
|
-
|
|
85
|
-
${({ $selected }) =>
|
|
86
|
-
$selected &&
|
|
87
|
-
`
|
|
88
|
-
opacity: 1;
|
|
89
|
-
`}
|
|
90
|
-
`;
|
|
91
|
-
|
|
92
|
-
export const StyledCardLink = styled.a<TypeCardLink>`
|
|
93
|
-
font-family: ${(p) => p.theme.fontFamily};
|
|
94
|
-
font-weight: ${(p) => p.theme.fontWeights.bold};
|
|
95
|
-
color: ${(p) => p.theme.colors.text.headline};
|
|
96
|
-
${(p) => p.theme.typography[400]};
|
|
97
|
-
|
|
98
|
-
${color}
|
|
99
|
-
${typography}
|
|
100
|
-
`;
|
|
101
|
-
|
|
102
|
-
export const StyledCard = styled.div<TypeStyledCard>`
|
|
103
|
-
position: relative;
|
|
104
|
-
display: flex;
|
|
105
|
-
flex-direction: column;
|
|
106
|
-
box-sizing: border-box;
|
|
107
|
-
margin: 0;
|
|
108
|
-
background: ${({ theme }) => theme.colors.container.background.base};
|
|
109
|
-
border: ${({ theme }) => theme.borderWidths[500]} solid
|
|
110
|
-
${({ theme }) => theme.colors.container.border.base};
|
|
111
|
-
padding: ${({ theme, $compositionalComponents }) =>
|
|
112
|
-
$compositionalComponents ? 0 : theme.space[400]};
|
|
113
|
-
border-radius: ${({ theme }) => theme.radii.outer};
|
|
114
|
-
transition: box-shadow ${({ theme }) => theme.duration.medium},
|
|
115
|
-
border ${({ theme }) => theme.duration.medium};
|
|
116
|
-
|
|
117
|
-
&[role="button"],
|
|
118
|
-
&[role="checkbox"] {
|
|
119
|
-
cursor: pointer;
|
|
120
|
-
|
|
121
|
-
&:hover {
|
|
122
|
-
box-shadow: ${({ theme, $elevation = "low" }) =>
|
|
123
|
-
theme.shadows[$elevation]};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
${({ $isRoleLink, theme, $elevation = "low" }) =>
|
|
128
|
-
$isRoleLink &&
|
|
129
|
-
`
|
|
130
|
-
cursor: pointer;
|
|
131
|
-
|
|
132
|
-
&:hover {
|
|
133
|
-
box-shadow: ${theme.shadows[$elevation]};
|
|
134
|
-
}
|
|
135
|
-
`}
|
|
136
|
-
|
|
137
|
-
&:focus-within {
|
|
138
|
-
${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}
|
|
139
|
-
${StyledCardLink}:focus {
|
|
140
|
-
border: none;
|
|
141
|
-
box-shadow: none;
|
|
142
|
-
outline: none;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
&:focus {
|
|
147
|
-
${focusRing}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
${({ $disabled }) =>
|
|
151
|
-
$disabled &&
|
|
152
|
-
`
|
|
153
|
-
${disabled}
|
|
154
|
-
`}
|
|
155
|
-
|
|
156
|
-
${({ $selected, theme }) =>
|
|
157
|
-
$selected &&
|
|
158
|
-
`
|
|
159
|
-
border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected};
|
|
160
|
-
`}
|
|
161
|
-
|
|
162
|
-
${border}
|
|
163
|
-
${color}
|
|
164
|
-
${flexbox}
|
|
165
|
-
${grid}
|
|
166
|
-
${layout}
|
|
167
|
-
${position}
|
|
168
|
-
${space}
|
|
169
|
-
`;
|
|
170
|
-
|
|
171
|
-
export const StyledCardAffordance = styled(Icon)`
|
|
172
|
-
${StyledCard}:hover & {
|
|
173
|
-
transform: translateX(${(p) => p.theme.space[200]});
|
|
174
|
-
}
|
|
175
|
-
transition: ${(p) => p.theme.duration.medium};
|
|
176
|
-
`;
|
package/src/subComponents.tsx
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import React, { useContext } from "react";
|
|
2
|
-
import { useChildContext, SubComponentContext } from "./utils";
|
|
3
|
-
import type {
|
|
4
|
-
TypeCardLink,
|
|
5
|
-
TypeSharedCardSystemProps,
|
|
6
|
-
TypeStyledSelectedIcon,
|
|
7
|
-
} from "./CardTypes";
|
|
8
|
-
import {
|
|
9
|
-
StyledCardContent,
|
|
10
|
-
StyledCardHeader,
|
|
11
|
-
StyledCardFooter,
|
|
12
|
-
StyledSelectedIcon,
|
|
13
|
-
SelectedIconWrapper,
|
|
14
|
-
StyledCardAffordance,
|
|
15
|
-
StyledCardLink,
|
|
16
|
-
} from "./styles";
|
|
17
|
-
|
|
18
|
-
interface TypeSharedSubComponentProps extends TypeSharedCardSystemProps {
|
|
19
|
-
children?: React.ReactNode;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const CardContent = ({
|
|
23
|
-
children,
|
|
24
|
-
...rest
|
|
25
|
-
}: TypeSharedSubComponentProps) => {
|
|
26
|
-
// TODO: It could be cool to possibly adjust the context to include an array of names of child components.
|
|
27
|
-
// Then, if CardHeader or CardFooter aren't used with CardContent throw an error.
|
|
28
|
-
useChildContext();
|
|
29
|
-
return <StyledCardContent {...rest}>{children}</StyledCardContent>;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const CardHeader = ({
|
|
33
|
-
children,
|
|
34
|
-
...rest
|
|
35
|
-
}: TypeSharedSubComponentProps) => {
|
|
36
|
-
useChildContext();
|
|
37
|
-
return <StyledCardHeader {...rest}>{children}</StyledCardHeader>;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const CardFooter = ({
|
|
41
|
-
children,
|
|
42
|
-
...rest
|
|
43
|
-
}: TypeSharedSubComponentProps) => {
|
|
44
|
-
useChildContext();
|
|
45
|
-
return <StyledCardFooter {...rest}>{children}</StyledCardFooter>;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
interface TypeSelectedIconProps {
|
|
49
|
-
$selected?: TypeStyledSelectedIcon["$selected"];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const SelectedIcon = ({ $selected }: TypeSelectedIconProps) => {
|
|
53
|
-
return (
|
|
54
|
-
<SelectedIconWrapper>
|
|
55
|
-
<StyledSelectedIcon
|
|
56
|
-
aria-hidden
|
|
57
|
-
color="icon.base"
|
|
58
|
-
name="circle-check-solid"
|
|
59
|
-
$selected={$selected}
|
|
60
|
-
/>
|
|
61
|
-
</SelectedIconWrapper>
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const CardAffordance = ({ ...rest }) => {
|
|
66
|
-
return (
|
|
67
|
-
<StyledCardAffordance
|
|
68
|
-
{...rest}
|
|
69
|
-
size="mini"
|
|
70
|
-
name="arrow-right-solid"
|
|
71
|
-
// TODO: probably need to make this available to the top level for external links https://sprout.atlassian.net/browse/DS-2223
|
|
72
|
-
aria-hidden
|
|
73
|
-
/>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const CardLink = ({
|
|
78
|
-
affordance,
|
|
79
|
-
children,
|
|
80
|
-
external = false,
|
|
81
|
-
color,
|
|
82
|
-
...rest
|
|
83
|
-
}: React.PropsWithChildren<TypeCardLink>) => {
|
|
84
|
-
const { href, linkRef } = useContext(SubComponentContext);
|
|
85
|
-
|
|
86
|
-
// Because we are hijacking Card click event to directly click this link, we need to stop propagation to avoid a double click event.
|
|
87
|
-
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
88
|
-
e.stopPropagation();
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<StyledCardLink
|
|
93
|
-
{...rest}
|
|
94
|
-
target={external ? "_blank" : undefined}
|
|
95
|
-
rel={external ? "noreferrer" : undefined}
|
|
96
|
-
href={href}
|
|
97
|
-
onClick={handleClick}
|
|
98
|
-
ref={linkRef}
|
|
99
|
-
// TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.
|
|
100
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
101
|
-
// @ts-ignore
|
|
102
|
-
color={color}
|
|
103
|
-
>
|
|
104
|
-
<>
|
|
105
|
-
{children}
|
|
106
|
-
{affordance ? <CardAffordance ml={300} /> : null}
|
|
107
|
-
</>
|
|
108
|
-
</StyledCardLink>
|
|
109
|
-
);
|
|
110
|
-
};
|