@sproutsocial/seeds-react-accordion 0.1.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.
@@ -0,0 +1,188 @@
1
+ import { StyledRadixAccordionTrigger, TitleStyles } from "./styles";
2
+ import {
3
+ type TypeAccordionSystemProps,
4
+ type TypeRelatedAction,
5
+ type TypeOverflowMenuConfig,
6
+ } from "./AccordionTypes";
7
+ import { FlexCenter, StyledAccordionArea, TriggerContainer } from "./styles";
8
+ import { Box } from "@sproutsocial/seeds-react-box";
9
+ import { Button } from "@sproutsocial/seeds-react-button";
10
+ import { Icon } from "@sproutsocial/seeds-react-icon";
11
+ import {
12
+ ActionMenu,
13
+ MenuContent,
14
+ MenuItem,
15
+ MenuGroup,
16
+ MenuToggleButton,
17
+ } from "@sproutsocial/seeds-react-menu";
18
+ import { useContext } from "react";
19
+ import { AccordionContext } from "./Accordion";
20
+
21
+ const MAX_RELATED_ACTIONS = 2;
22
+
23
+ interface TypeAccordionTriggerProps extends TypeAccordionSystemProps {
24
+ title: string;
25
+ leftSlot?: React.ReactNode;
26
+ relatedActions?: TypeRelatedAction[];
27
+ overflowMenu?: TypeOverflowMenuConfig;
28
+ rightSlot?: React.ReactNode;
29
+ }
30
+
31
+ export const AccordionTrigger = ({
32
+ children,
33
+ leftSlot,
34
+ relatedActions,
35
+ overflowMenu,
36
+ rightSlot,
37
+ title,
38
+ ...rest
39
+ }: TypeAccordionTriggerProps) => {
40
+ const { triggerIcon, triggerPosition, styled } = useContext(AccordionContext);
41
+
42
+ // Validate and limit related actions
43
+ const validatedActions = relatedActions?.slice(0, MAX_RELATED_ACTIONS);
44
+
45
+ // Extract system props to distribute to appropriate container
46
+ const {
47
+ color,
48
+ padding,
49
+ paddingBottom,
50
+ paddingTop,
51
+ paddingX,
52
+ paddingY,
53
+ paddingLeft,
54
+ paddingRight,
55
+ p,
56
+ pb,
57
+ pt,
58
+ pr,
59
+ pl,
60
+ px,
61
+ py,
62
+ fontFamily,
63
+ fontStyle,
64
+ fontWeight,
65
+ lineHeight,
66
+ textAlign,
67
+ ...triggerProps
68
+ } = rest;
69
+
70
+ const spacingProps = {
71
+ padding,
72
+ paddingBottom,
73
+ paddingTop,
74
+ paddingX,
75
+ paddingY,
76
+ paddingLeft,
77
+ paddingRight,
78
+ p,
79
+ pb,
80
+ pt,
81
+ pr,
82
+ pl,
83
+ px,
84
+ py,
85
+ };
86
+
87
+ // When you destructure color from rest, it might be null, which is incompatible with what the styled component expects. We need to filter out null or undefined values from typographyProps before spreading them.
88
+
89
+ const typographyProps = Object.fromEntries(
90
+ Object.entries({
91
+ color,
92
+ fontFamily,
93
+ fontStyle,
94
+ fontWeight,
95
+ lineHeight,
96
+ textAlign,
97
+ }).filter(([_, value]) => value != null)
98
+ );
99
+
100
+ // Render overflow menu from config
101
+ const renderedOverflowMenu = overflowMenu && (
102
+ <ActionMenu
103
+ menuToggleElement={
104
+ <MenuToggleButton
105
+ aria-label={overflowMenu["aria-label"]}
106
+ appearance="unstyled"
107
+ >
108
+ <Icon name="ellipsis-horizontal-outline" aria-hidden="true" />
109
+ </MenuToggleButton>
110
+ }
111
+ >
112
+ <MenuContent>
113
+ <MenuGroup id="overflow-actions">
114
+ {overflowMenu.items.map((item, index) => {
115
+ const { iconName, id, onClick, children, ...menuItemProps } = item;
116
+ return (
117
+ <MenuItem
118
+ key={id || `overflow-item-${index}`}
119
+ id={id || `overflow-item-${index}`}
120
+ onClick={onClick}
121
+ {...menuItemProps}
122
+ >
123
+ {iconName ? (
124
+ <Box display="flex" alignItems="center" gap="300">
125
+ <Icon name={iconName} />
126
+ {children}
127
+ </Box>
128
+ ) : (
129
+ children
130
+ )}
131
+ </MenuItem>
132
+ );
133
+ })}
134
+ </MenuGroup>
135
+ </MenuContent>
136
+ </ActionMenu>
137
+ );
138
+
139
+ // Render related actions from config
140
+ const renderedRelatedActions = validatedActions &&
141
+ validatedActions.length > 0 && (
142
+ <Box display="flex">
143
+ {validatedActions.map((action, index) => (
144
+ <Button
145
+ key={`${action.iconName}-${index}`}
146
+ onClick={action.onClick}
147
+ aria-label={action["aria-label"]}
148
+ >
149
+ <Icon name={action.iconName} aria-hidden="true" />
150
+ </Button>
151
+ ))}
152
+ </Box>
153
+ );
154
+
155
+ return (
156
+ <TriggerContainer data-styled={styled} {...triggerProps}>
157
+ <StyledRadixAccordionTrigger data-styled={styled} {...spacingProps}>
158
+ {triggerPosition === "right" ? (
159
+ <StyledAccordionArea>
160
+ <FlexCenter>
161
+ {leftSlot}
162
+ <TitleStyles data-styled={styled} {...typographyProps}>
163
+ {title}
164
+ </TitleStyles>
165
+ {rightSlot}
166
+ </FlexCenter>
167
+ {triggerIcon}
168
+ </StyledAccordionArea>
169
+ ) : (
170
+ <StyledAccordionArea>
171
+ <FlexCenter>
172
+ <Box mr={300}>{triggerIcon}</Box>
173
+ {leftSlot}
174
+ <TitleStyles data-styled={styled} {...typographyProps}>
175
+ {title}
176
+ </TitleStyles>
177
+ </FlexCenter>
178
+ {rightSlot}
179
+ </StyledAccordionArea>
180
+ )}
181
+ </StyledRadixAccordionTrigger>
182
+ <Box mr={300} display="flex">
183
+ {renderedOverflowMenu}
184
+ {renderedRelatedActions}
185
+ </Box>
186
+ </TriggerContainer>
187
+ );
188
+ };
@@ -0,0 +1,54 @@
1
+ import * as React from "react";
2
+ import {
3
+ type TypeSystemCommonProps,
4
+ type TypeBorderSystemProps,
5
+ type TypeFlexboxSystemProps,
6
+ type TypeLayoutSystemProps,
7
+ type TypeStyledComponentsCommonProps,
8
+ type TypeTypographySystemProps,
9
+ } from "@sproutsocial/seeds-react-system-props";
10
+ import { type TypeIconName } from "@sproutsocial/seeds-react-icon";
11
+ import { type TypeMenuItemProps } from "@sproutsocial/seeds-react-menu";
12
+
13
+ export interface TypeAccordionSystemProps
14
+ extends Omit<React.ComponentPropsWithoutRef<"div">, "color">,
15
+ TypeStyledComponentsCommonProps,
16
+ TypeSystemCommonProps,
17
+ TypeBorderSystemProps,
18
+ TypeFlexboxSystemProps,
19
+ TypeLayoutSystemProps,
20
+ TypeTypographySystemProps {}
21
+
22
+ export interface TypeAccordionProps {
23
+ children?: React.ReactNode;
24
+ collapsible?: boolean;
25
+ defaultValue: string | [string];
26
+ triggerIcon?: React.ReactNode;
27
+ triggerPosition?: "left" | "right";
28
+ type?: "single" | "multiple";
29
+ styled?: boolean;
30
+ }
31
+
32
+ export interface TypeRelatedAction {
33
+ iconName: TypeIconName;
34
+ onClick: () => void;
35
+ "aria-label": string;
36
+ }
37
+
38
+ export interface TypeOverflowMenuItem extends TypeMenuItemProps {
39
+ iconName?: TypeIconName;
40
+ }
41
+
42
+ export interface TypeOverflowMenuConfig {
43
+ /** Menu items to be rendered in the overflow menu */
44
+ items: TypeOverflowMenuItem[];
45
+ /** Aria label for the overflow menu trigger button. Defaults to "More actions" */
46
+ "aria-label"?: string;
47
+ }
48
+
49
+ export interface TypeAccordionItemProps {
50
+ children: React.ReactNode;
51
+ relatedActions?: TypeRelatedAction[];
52
+ overflowMenu?: TypeOverflowMenuConfig;
53
+ value: string;
54
+ }
@@ -0,0 +1,419 @@
1
+ import React from "react";
2
+ import {
3
+ render,
4
+ screen,
5
+ fireEvent,
6
+ } from "@sproutsocial/seeds-react-testing-library";
7
+ import { Accordion } from "../Accordion";
8
+ import { AccordionItem } from "../AccordionItem";
9
+ import { AccordionTrigger } from "../AccordionTrigger";
10
+ import { AccordionContent } from "../AccordionContent";
11
+
12
+ describe("Accordion", () => {
13
+ it("renders accordion with title", () => {
14
+ render(
15
+ <Accordion defaultValue={["item-1"]}>
16
+ <AccordionItem value="item-1">
17
+ <AccordionTrigger title="Test Title" />
18
+ <AccordionContent>Content</AccordionContent>
19
+ </AccordionItem>
20
+ </Accordion>
21
+ );
22
+
23
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
24
+ });
25
+
26
+ describe("relatedActions", () => {
27
+ it("renders action buttons with proper aria-labels", () => {
28
+ render(
29
+ <Accordion defaultValue={["item-1"]}>
30
+ <AccordionItem value="item-1">
31
+ <AccordionTrigger
32
+ title="Test"
33
+ relatedActions={[
34
+ {
35
+ iconName: "alarm-clock",
36
+ onClick: jest.fn(),
37
+ "aria-label": "Set alarm",
38
+ },
39
+ {
40
+ iconName: "ellipsis-horizontal-outline",
41
+ onClick: jest.fn(),
42
+ "aria-label": "More options",
43
+ },
44
+ ]}
45
+ />
46
+ <AccordionContent>Content</AccordionContent>
47
+ </AccordionItem>
48
+ </Accordion>
49
+ );
50
+
51
+ expect(screen.getByLabelText("Set alarm")).toBeInTheDocument();
52
+ expect(screen.getByLabelText("More options")).toBeInTheDocument();
53
+ });
54
+
55
+ it("limits related actions to maximum of 3", () => {
56
+ render(
57
+ <Accordion defaultValue={["item-1"]}>
58
+ <AccordionItem value="item-1">
59
+ <AccordionTrigger
60
+ title="Test"
61
+ relatedActions={[
62
+ {
63
+ iconName: "alarm-clock",
64
+ onClick: jest.fn(),
65
+ "aria-label": "Action 1",
66
+ },
67
+ {
68
+ iconName: "ellipsis-horizontal-outline",
69
+ onClick: jest.fn(),
70
+ "aria-label": "Action 2",
71
+ },
72
+ {
73
+ iconName: "alarm-clock",
74
+ onClick: jest.fn(),
75
+ "aria-label": "Action 3",
76
+ },
77
+ {
78
+ iconName: "ellipsis-horizontal-outline",
79
+ onClick: jest.fn(),
80
+ "aria-label": "Action 4",
81
+ },
82
+ ]}
83
+ />
84
+ <AccordionContent>Content</AccordionContent>
85
+ </AccordionItem>
86
+ </Accordion>
87
+ );
88
+
89
+ expect(screen.getByLabelText("Action 1")).toBeInTheDocument();
90
+ expect(screen.getByLabelText("Action 2")).toBeInTheDocument();
91
+ expect(screen.getByLabelText("Action 3")).toBeInTheDocument();
92
+ expect(screen.queryByLabelText("Action 4")).not.toBeInTheDocument();
93
+ });
94
+
95
+ it("renders icons with aria-hidden for accessibility", () => {
96
+ render(
97
+ <Accordion defaultValue={["item-1"]}>
98
+ <AccordionItem value="item-1">
99
+ <AccordionTrigger
100
+ title="Test"
101
+ relatedActions={[
102
+ {
103
+ iconName: "alarm-clock",
104
+ onClick: jest.fn(),
105
+ "aria-label": "Set alarm",
106
+ },
107
+ ]}
108
+ />
109
+ <AccordionContent>Content</AccordionContent>
110
+ </AccordionItem>
111
+ </Accordion>
112
+ );
113
+
114
+ const button = screen.getByLabelText("Set alarm");
115
+ const icon = button.querySelector('[aria-hidden="true"]');
116
+ expect(icon).toBeInTheDocument();
117
+ });
118
+
119
+ it("calls onClick handler when action button is clicked", () => {
120
+ const mockOnClick = jest.fn();
121
+ render(
122
+ <Accordion defaultValue={["item-1"]}>
123
+ <AccordionItem value="item-1">
124
+ <AccordionTrigger
125
+ title="Test"
126
+ relatedActions={[
127
+ {
128
+ iconName: "alarm-clock",
129
+ onClick: mockOnClick,
130
+ "aria-label": "Set alarm",
131
+ },
132
+ ]}
133
+ />
134
+ <AccordionContent>Content</AccordionContent>
135
+ </AccordionItem>
136
+ </Accordion>
137
+ );
138
+
139
+ const actionButton = screen.getByLabelText("Set alarm");
140
+ fireEvent.click(actionButton);
141
+
142
+ expect(mockOnClick).toHaveBeenCalledTimes(1);
143
+ });
144
+
145
+ it("does not toggle accordion when clicking action button", () => {
146
+ const mockOnClick = jest.fn();
147
+ render(
148
+ <Accordion defaultValue={["item-1"]}>
149
+ <AccordionItem value="item-1">
150
+ <AccordionTrigger
151
+ title="Test"
152
+ relatedActions={[
153
+ {
154
+ iconName: "alarm-clock",
155
+ onClick: mockOnClick,
156
+ "aria-label": "Set alarm",
157
+ },
158
+ ]}
159
+ />
160
+ <AccordionContent>Content</AccordionContent>
161
+ </AccordionItem>
162
+ </Accordion>
163
+ );
164
+
165
+ // Verify accordion is open initially
166
+ expect(screen.getByText("Content")).toBeInTheDocument();
167
+
168
+ // Click the action button
169
+ const actionButton = screen.getByLabelText("Set alarm");
170
+ fireEvent.click(actionButton);
171
+
172
+ // Accordion should still be open
173
+ expect(screen.getByText("Content")).toBeInTheDocument();
174
+ expect(mockOnClick).toHaveBeenCalled();
175
+ });
176
+
177
+ it("handles multiple onClick handlers correctly", () => {
178
+ const mockOnClick1 = jest.fn();
179
+ const mockOnClick2 = jest.fn();
180
+ render(
181
+ <Accordion defaultValue={["item-1"]}>
182
+ <AccordionItem value="item-1">
183
+ <AccordionTrigger
184
+ title="Test"
185
+ relatedActions={[
186
+ {
187
+ iconName: "alarm-clock",
188
+ onClick: mockOnClick1,
189
+ "aria-label": "Action 1",
190
+ },
191
+ {
192
+ iconName: "ellipsis-horizontal-outline",
193
+ onClick: mockOnClick2,
194
+ "aria-label": "Action 2",
195
+ },
196
+ ]}
197
+ />
198
+ <AccordionContent>Content</AccordionContent>
199
+ </AccordionItem>
200
+ </Accordion>
201
+ );
202
+
203
+ fireEvent.click(screen.getByLabelText("Action 1"));
204
+ expect(mockOnClick1).toHaveBeenCalledTimes(1);
205
+ expect(mockOnClick2).not.toHaveBeenCalled();
206
+
207
+ fireEvent.click(screen.getByLabelText("Action 2"));
208
+ expect(mockOnClick1).toHaveBeenCalledTimes(1);
209
+ expect(mockOnClick2).toHaveBeenCalledTimes(1);
210
+ });
211
+ });
212
+
213
+ describe("trigger icon", () => {
214
+ it("renders default chevron-down icon", () => {
215
+ const { container } = render(
216
+ <Accordion defaultValue={["item-1"]}>
217
+ <AccordionItem value="item-1">
218
+ <AccordionTrigger title="Test Title" />
219
+ <AccordionContent>Content</AccordionContent>
220
+ </AccordionItem>
221
+ </Accordion>
222
+ );
223
+
224
+ const icon = container.querySelector(".triggerIcon");
225
+ expect(icon).toBeInTheDocument();
226
+ });
227
+
228
+ it("renders custom trigger icon", () => {
229
+ const CustomIcon = () => <div data-testid="custom-icon">Custom Icon</div>;
230
+
231
+ render(
232
+ <Accordion defaultValue={["item-1"]} triggerIcon={<CustomIcon />}>
233
+ <AccordionItem value="item-1">
234
+ <AccordionTrigger title="Test Title" />
235
+ <AccordionContent>Content</AccordionContent>
236
+ </AccordionItem>
237
+ </Accordion>
238
+ );
239
+
240
+ expect(screen.getByTestId("custom-icon")).toBeInTheDocument();
241
+ });
242
+
243
+ it("positions trigger icon on the right by default", () => {
244
+ const { container } = render(
245
+ <Accordion defaultValue={["item-1"]}>
246
+ <AccordionItem value="item-1">
247
+ <AccordionTrigger title="Test Title" />
248
+ <AccordionContent>Content</AccordionContent>
249
+ </AccordionItem>
250
+ </Accordion>
251
+ );
252
+
253
+ const icon = container.querySelector(".triggerIcon");
254
+ expect(icon).toBeInTheDocument();
255
+ // Icon should be rendered after title (right position)
256
+ const trigger = screen.getByText("Test Title").closest("button");
257
+ const iconParent = icon?.parentElement ?? null;
258
+ expect(trigger).toContainElement(iconParent);
259
+ });
260
+
261
+ it("positions trigger icon on the left when triggerPosition is 'left'", () => {
262
+ const { container } = render(
263
+ <Accordion defaultValue={["item-1"]} triggerPosition="left">
264
+ <AccordionItem value="item-1">
265
+ <AccordionTrigger title="Test Title" />
266
+ <AccordionContent>Content</AccordionContent>
267
+ </AccordionItem>
268
+ </Accordion>
269
+ );
270
+
271
+ const icon = container.querySelector(".triggerIcon");
272
+ expect(icon).toBeInTheDocument();
273
+ const trigger = screen.getByText("Test Title").closest("button");
274
+ const iconParent = icon?.parentElement ?? null;
275
+ expect(trigger).toContainElement(iconParent);
276
+ });
277
+
278
+ it("positions trigger icon on the right when triggerPosition is 'right'", () => {
279
+ const { container } = render(
280
+ <Accordion defaultValue={["item-1"]} triggerPosition="right">
281
+ <AccordionItem value="item-1">
282
+ <AccordionTrigger title="Test Title" />
283
+ <AccordionContent>Content</AccordionContent>
284
+ </AccordionItem>
285
+ </Accordion>
286
+ );
287
+
288
+ const icon = container.querySelector(".triggerIcon");
289
+ expect(icon).toBeInTheDocument();
290
+ });
291
+ });
292
+
293
+ describe("slots", () => {
294
+ it("renders leftSlot content", () => {
295
+ render(
296
+ <Accordion defaultValue={["item-1"]}>
297
+ <AccordionItem value="item-1">
298
+ <AccordionTrigger
299
+ title="Test Title"
300
+ leftSlot={<div data-testid="left-content">Left Content</div>}
301
+ />
302
+ <AccordionContent>Content</AccordionContent>
303
+ </AccordionItem>
304
+ </Accordion>
305
+ );
306
+
307
+ expect(screen.getByTestId("left-content")).toBeInTheDocument();
308
+ expect(screen.getByText("Left Content")).toBeInTheDocument();
309
+ });
310
+
311
+ it("renders rightSlot content", () => {
312
+ render(
313
+ <Accordion defaultValue={["item-1"]}>
314
+ <AccordionItem value="item-1">
315
+ <AccordionTrigger
316
+ title="Test Title"
317
+ rightSlot={<div data-testid="right-content">Right Content</div>}
318
+ />
319
+ <AccordionContent>Content</AccordionContent>
320
+ </AccordionItem>
321
+ </Accordion>
322
+ );
323
+
324
+ expect(screen.getByTestId("right-content")).toBeInTheDocument();
325
+ expect(screen.getByText("Right Content")).toBeInTheDocument();
326
+ });
327
+
328
+ it("renders both leftSlot and rightSlot together", () => {
329
+ render(
330
+ <Accordion defaultValue={["item-1"]}>
331
+ <AccordionItem value="item-1">
332
+ <AccordionTrigger
333
+ title="Test Title"
334
+ leftSlot={<div data-testid="left-content">Left Content</div>}
335
+ rightSlot={<div data-testid="right-content">Right Content</div>}
336
+ />
337
+ <AccordionContent>Content</AccordionContent>
338
+ </AccordionItem>
339
+ </Accordion>
340
+ );
341
+
342
+ expect(screen.getByTestId("left-content")).toBeInTheDocument();
343
+ expect(screen.getByTestId("right-content")).toBeInTheDocument();
344
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
345
+ });
346
+
347
+ it("renders slots with complex components", () => {
348
+ const LeftComponent = () => (
349
+ <div data-testid="left-complex">
350
+ <span>Icon</span>
351
+ <span>Badge</span>
352
+ </div>
353
+ );
354
+ const RightComponent = () => (
355
+ <div data-testid="right-complex">
356
+ <button>Action</button>
357
+ </div>
358
+ );
359
+
360
+ render(
361
+ <Accordion defaultValue={["item-1"]}>
362
+ <AccordionItem value="item-1">
363
+ <AccordionTrigger
364
+ title="Test Title"
365
+ leftSlot={<LeftComponent />}
366
+ rightSlot={<RightComponent />}
367
+ />
368
+ <AccordionContent>Content</AccordionContent>
369
+ </AccordionItem>
370
+ </Accordion>
371
+ );
372
+
373
+ expect(screen.getByTestId("left-complex")).toBeInTheDocument();
374
+ expect(screen.getByTestId("right-complex")).toBeInTheDocument();
375
+ expect(screen.getByText("Icon")).toBeInTheDocument();
376
+ expect(screen.getByText("Badge")).toBeInTheDocument();
377
+ expect(screen.getByText("Action")).toBeInTheDocument();
378
+ });
379
+
380
+ it("positions leftSlot before title and rightSlot after title when triggerPosition is right", () => {
381
+ render(
382
+ <Accordion defaultValue={["item-1"]} triggerPosition="right">
383
+ <AccordionItem value="item-1">
384
+ <AccordionTrigger
385
+ title="Test Title"
386
+ leftSlot={<span data-testid="left">Left</span>}
387
+ rightSlot={<span data-testid="right">Right</span>}
388
+ />
389
+ <AccordionContent>Content</AccordionContent>
390
+ </AccordionItem>
391
+ </Accordion>
392
+ );
393
+
394
+ const trigger = screen.getByText("Test Title").closest("button");
395
+ expect(trigger).toBeInTheDocument();
396
+ expect(screen.getByTestId("left")).toBeInTheDocument();
397
+ expect(screen.getByTestId("right")).toBeInTheDocument();
398
+ });
399
+
400
+ it("renders slots correctly when triggerPosition is left", () => {
401
+ render(
402
+ <Accordion defaultValue={["item-1"]} triggerPosition="left">
403
+ <AccordionItem value="item-1">
404
+ <AccordionTrigger
405
+ title="Test Title"
406
+ leftSlot={<span data-testid="left">Left</span>}
407
+ rightSlot={<span data-testid="right">Right</span>}
408
+ />
409
+ <AccordionContent>Content</AccordionContent>
410
+ </AccordionItem>
411
+ </Accordion>
412
+ );
413
+
414
+ expect(screen.getByTestId("left")).toBeInTheDocument();
415
+ expect(screen.getByTestId("right")).toBeInTheDocument();
416
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
417
+ });
418
+ });
419
+ });
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Accordion } from "./Accordion";
2
+ import { AccordionItem } from "./AccordionItem";
3
+ import { AccordionContent } from "./AccordionContent";
4
+
5
+ export { Accordion, AccordionItem, AccordionContent };
6
+ export * from "./AccordionTypes";
@@ -0,0 +1,7 @@
1
+ import "styled-components";
2
+ import { TypeTheme } from "@sproutsocial/seeds-react-theme";
3
+
4
+ declare module "styled-components" {
5
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
6
+ export interface DefaultTheme extends TypeTheme {}
7
+ }