@sproutsocial/seeds-react-tabs 1.0.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 ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@sproutsocial/seeds-react-tabs",
3
+ "version": "1.0.0",
4
+ "description": "Seeds React Tabs",
5
+ "author": "Sprout Social, Inc.",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsup --dts",
12
+ "build:debug": "tsup --dts --metafile",
13
+ "dev": "tsup --watch --dts",
14
+ "clean": "rm -rf .turbo dist",
15
+ "clean:modules": "rm -rf node_modules",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch --coverage=false"
19
+ },
20
+ "dependencies": {
21
+ "@sproutsocial/seeds-react-theme": "*",
22
+ "@sproutsocial/seeds-react-system-props": "*",
23
+ "@sproutsocial/seeds-react-button": "*"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "^18.0.0",
27
+ "@types/styled-components": "^5.1.26",
28
+ "@sproutsocial/eslint-config-seeds": "*",
29
+ "react": "^18.0.0",
30
+ "styled-components": "^5.2.3",
31
+ "tsup": "^8.0.2",
32
+ "typescript": "^5.6.2",
33
+ "@sproutsocial/seeds-tsconfig": "*",
34
+ "@sproutsocial/seeds-testing": "*",
35
+ "@sproutsocial/seeds-react-testing-library": "*"
36
+ },
37
+ "peerDependencies": {
38
+ "styled-components": "^5.2.3"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ }
43
+ }
@@ -0,0 +1,122 @@
1
+ import React, { useState } from "react";
2
+ import { Button } from "@sproutsocial/seeds-react-button";
3
+ import { Icon, type TypeIconName } from "@sproutsocial/seeds-react-icon";
4
+ import { Tabs, type TypeTabsProps } from "./";
5
+ import { Text } from "@sproutsocial/seeds-react-text";
6
+ import type { Meta, StoryObj } from "@storybook/react";
7
+
8
+ interface TypeTabData {
9
+ id: string;
10
+ text: string;
11
+ icon: TypeIconName;
12
+ }
13
+
14
+ interface TypeTabsStoryState {
15
+ selected: string;
16
+ tabs?: TypeTabData[];
17
+ }
18
+
19
+ const meta: Meta<typeof Tabs> = {
20
+ title: "Components/Tabs",
21
+ component: Tabs,
22
+ args: {
23
+ fullWidth: false,
24
+ },
25
+ };
26
+
27
+ export default meta;
28
+
29
+ type Story = StoryObj<typeof Tabs>;
30
+
31
+ export const Default: Story = {
32
+ render: (args) => {
33
+ const [selectedId, setSelectedId] = useState("notifications");
34
+
35
+ return (
36
+ <Tabs
37
+ selectedId={selectedId}
38
+ onSelect={(selected) => setSelectedId(selected)}
39
+ fullWidth={args.fullWidth}
40
+ >
41
+ <Tabs.Button id="notifications">
42
+ <Icon name="bell-outline" mr="12px" aria-hidden />
43
+ <Text size="300" fontWeight="600">
44
+ Notifications
45
+ </Text>
46
+ </Tabs.Button>
47
+ <Tabs.Button id="issues">
48
+ <Icon name="triangle-exclamation-outline" mr="12px" aria-hidden />
49
+ <Text size="300" fontWeight="600">
50
+ Issues
51
+ </Text>
52
+ </Tabs.Button>
53
+ </Tabs>
54
+ );
55
+ },
56
+ name: "Default",
57
+ };
58
+
59
+ export const RemoveATab: Story = {
60
+ render: (args) => {
61
+ const [state, setState] = useState<TypeTabsStoryState>({
62
+ selected: "notifications",
63
+ tabs: [
64
+ {
65
+ id: "notifications",
66
+ text: "Notifications",
67
+ icon: "bell-outline",
68
+ },
69
+ {
70
+ id: "issues",
71
+ text: "Issues",
72
+ icon: "triangle-exclamation-outline",
73
+ },
74
+ {
75
+ id: "fun",
76
+ text: "Fun Tab",
77
+ icon: "x-twitter",
78
+ },
79
+ {
80
+ id: "drafts",
81
+ text: "Drafts",
82
+ icon: "paper-outline",
83
+ },
84
+ ],
85
+ });
86
+
87
+ return (
88
+ <React.Fragment>
89
+ <Button
90
+ onClick={() =>
91
+ setState({
92
+ ...state,
93
+ tabs: state.tabs?.slice(1),
94
+ })
95
+ }
96
+ >
97
+ Remove a tab <Icon name="x-outline" aria-hidden />
98
+ </Button>
99
+ <Tabs
100
+ selectedId={state.selected}
101
+ onSelect={(selected) =>
102
+ setState({
103
+ ...state,
104
+ selected,
105
+ })
106
+ }
107
+ fullWidth={args.fullWidth}
108
+ >
109
+ {state.tabs?.map((tab) => (
110
+ <Tabs.Button key={tab.id} id={tab.id}>
111
+ <Icon name={tab.icon} mr="12px" aria-hidden />
112
+ <Text size="300" fontWeight="600">
113
+ {tab.text}
114
+ </Text>
115
+ </Tabs.Button>
116
+ ))}
117
+ </Tabs>
118
+ </React.Fragment>
119
+ );
120
+ },
121
+ name: "Remove a tab",
122
+ };
package/src/Tabs.tsx ADDED
@@ -0,0 +1,171 @@
1
+ import * as React from "react";
2
+ import Container, { TabItem, TabButton } from "./styles";
3
+ import type { TypeTabButtonsProps, TypeTabsProps } from "./TabsTypes";
4
+
5
+ interface TypeTabButtonRef {
6
+ current: null | React.ElementRef<"button">;
7
+ }
8
+ interface TypeTabButtonContext {
9
+ onActivate: (arg0: string) => void;
10
+ onTabMount: (id: string, ref: TypeTabButtonRef) => void;
11
+ onTabUpdate: (previousId: string, nextId: string) => void;
12
+ onTabUnmount: (id: string) => void;
13
+ selectedId: string;
14
+ fullWidth?: boolean;
15
+ }
16
+
17
+ const TabButtonContext = React.createContext<Partial<TypeTabButtonContext>>({});
18
+
19
+ class TabItemButton extends React.Component<TypeTabButtonsProps> {
20
+ static override contextType = TabButtonContext;
21
+ // @ts-notes - using the legacy syntax here because `declare` does not play well with babel
22
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
23
+ //@ts-ignore
24
+ context!: React.ContextType<typeof TabButtonContext>;
25
+
26
+ buttonRef: TypeTabButtonRef = React.createRef<React.ElementRef<"button">>();
27
+
28
+ override componentDidMount() {
29
+ this.context.onTabMount?.(this.props.id, this.buttonRef);
30
+ }
31
+
32
+ override componentDidUpdate(prevProps: TypeTabButtonsProps) {
33
+ if (prevProps.id !== this.props.id) {
34
+ this.context.onTabUpdate?.(prevProps.id, this.props.id);
35
+ }
36
+ }
37
+
38
+ override componentWillUnmount() {
39
+ this.context.onTabUnmount?.(this.props.id);
40
+ }
41
+
42
+ override render() {
43
+ const { children, id, ...rest } = this.props;
44
+ const { selectedId, onActivate, fullWidth } = this.context;
45
+ const isSelected = selectedId === id;
46
+
47
+ return (
48
+ <TabItem key={id} fullWidth={fullWidth} isSelected={isSelected}>
49
+ <TabButton
50
+ innerRef={this.buttonRef}
51
+ id={id}
52
+ onClick={() => onActivate?.(id)}
53
+ isSelected={isSelected}
54
+ tabIndex={isSelected ? 0 : -1}
55
+ fullWidth={fullWidth}
56
+ data-qa-tab-button={id}
57
+ data-qa-tab-button-state={isSelected}
58
+ aria-selected={isSelected}
59
+ role="tab"
60
+ // TODO: Add a TabPanel subcomponent for use with tabs
61
+ // aria-controls={tabPanelId}
62
+ {...rest}
63
+ >
64
+ {children}
65
+ </TabButton>
66
+ </TabItem>
67
+ );
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Render a group of buttons in a tab-heading style
73
+ */
74
+ class Tabs extends React.Component<TypeTabsProps> {
75
+ static Button = TabItemButton;
76
+ buttonRefs: Record<string, TypeTabButtonRef | null | undefined> = {};
77
+ tabList: string[] = [];
78
+
79
+ getSelectedId = () => this.props.selectedId;
80
+ onActivate = (id: string) => this.props.onSelect(id);
81
+ onTabMount = (id: string, ref: TypeTabButtonRef) => {
82
+ if (!this.tabList.includes(id)) {
83
+ this.tabList.push(id);
84
+ this.buttonRefs[id] = ref;
85
+ }
86
+ };
87
+
88
+ onTabUpdate = (previousId: string, newId: string) => {
89
+ if (this.tabList.includes(previousId)) {
90
+ this.tabList = this.tabList.map((id) => (id === previousId ? newId : id));
91
+ this.buttonRefs[newId] = this.buttonRefs[previousId];
92
+ this.buttonRefs[previousId] = undefined;
93
+ }
94
+ };
95
+
96
+ onTabUnmount = (id: string) => {
97
+ if (this.tabList.includes(id)) {
98
+ this.tabList = this.tabList.filter((currentId) => currentId !== id);
99
+ this.buttonRefs[id] = undefined;
100
+ }
101
+ };
102
+
103
+ selectNextTab = (id: string) => {
104
+ const buttonRef = this.buttonRefs[id];
105
+ this.props.onSelect(id);
106
+ buttonRef &&
107
+ buttonRef.current &&
108
+ buttonRef.current.focus &&
109
+ buttonRef.current.focus();
110
+ };
111
+
112
+ keyHandler = ({ keyCode }: React.KeyboardEvent<HTMLUListElement>) => {
113
+ switch (keyCode) {
114
+ // left arrow
115
+ case 37:
116
+ this.tabList.forEach((id, index) => {
117
+ if (id === this.getSelectedId()) {
118
+ const count = this.tabList.length;
119
+ const nextIndex = index - 1 >= 0 ? index - 1 : count - 1;
120
+ const nextId = this.tabList[nextIndex];
121
+ this.selectNextTab(nextId ? nextId : "");
122
+ }
123
+ });
124
+ break;
125
+
126
+ // right arrow
127
+ case 39:
128
+ this.tabList.forEach((id, index) => {
129
+ if (id === this.getSelectedId()) {
130
+ const count = this.tabList.length;
131
+ const nextIndex = index + 1 < count ? index + 1 : 0;
132
+ const nextId = this.tabList[nextIndex];
133
+ this.selectNextTab(nextId ? nextId : "");
134
+ }
135
+ });
136
+ break;
137
+
138
+ default:
139
+ break;
140
+ }
141
+ };
142
+
143
+ override render() {
144
+ const { children, qa, onSelect, ...rest } = this.props;
145
+ return (
146
+ <Container
147
+ data-qa-tabs=""
148
+ onKeyUp={this.keyHandler}
149
+ onSelect={onSelect}
150
+ role="tablist"
151
+ {...qa}
152
+ {...rest}
153
+ >
154
+ <TabButtonContext.Provider
155
+ value={{
156
+ selectedId: this.getSelectedId(),
157
+ fullWidth: this.props.fullWidth,
158
+ onActivate: this.onActivate,
159
+ onTabMount: this.onTabMount,
160
+ onTabUpdate: this.onTabUpdate,
161
+ onTabUnmount: this.onTabUnmount,
162
+ }}
163
+ >
164
+ {children}
165
+ </TabButtonContext.Provider>
166
+ </Container>
167
+ );
168
+ }
169
+ }
170
+
171
+ export default Tabs;
@@ -0,0 +1,31 @@
1
+ import * as React from "react";
2
+ import type {
3
+ TypeStyledComponentsCommonProps,
4
+ TypeSystemCommonProps,
5
+ } from "@sproutsocial/seeds-react-system-props";
6
+ import type { TypeButtonProps } from "@sproutsocial/seeds-react-button";
7
+
8
+ export interface TypeTabButtonsProps extends TypeButtonProps {
9
+ children: React.ReactNode;
10
+
11
+ /** Should be unique among sibling elements */
12
+ id: string;
13
+ }
14
+
15
+ export interface TypeTabsProps
16
+ extends TypeStyledComponentsCommonProps,
17
+ TypeSystemCommonProps,
18
+ Omit<
19
+ React.ComponentPropsWithoutRef<"ul">,
20
+ keyof TypeSystemCommonProps | "onSelect"
21
+ > {
22
+ children: React.ReactNode;
23
+ onSelect: (arg0: string) => void;
24
+
25
+ /** Whether or not the tabs should stretch to fill the width of their container */
26
+ fullWidth?: boolean;
27
+ qa?: object;
28
+
29
+ /** ID of the selected tab */
30
+ selectedId: string;
31
+ }
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import Tabs from "../";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ function TabsTypes() {
6
+ const tabOne = "Tab one";
7
+ const mockHandler = () => {};
8
+
9
+ return (
10
+ <>
11
+ <Tabs selectedId={tabOne} onSelect={mockHandler}>
12
+ <Tabs.Button id={tabOne}>{tabOne}</Tabs.Button>
13
+ </Tabs>
14
+ {/* @ts-expect-error - test that invalid onSelect is rejected */}
15
+ <Tabs selectedId={3} onSelect="invalid">
16
+ <Tabs.Button id={tabOne}>{tabOne}</Tabs.Button>
17
+ </Tabs>
18
+ </>
19
+ );
20
+ }
@@ -0,0 +1,271 @@
1
+ import * as React from "react";
2
+ import {
3
+ render,
4
+ fireEvent,
5
+ screen,
6
+ } from "@sproutsocial/seeds-react-testing-library";
7
+ import { Box } from "@sproutsocial/seeds-react-box";
8
+ import { Tabs, type TypeTabsProps } from "../";
9
+
10
+ const leftArrowKey = {
11
+ key: "ArrowLeft",
12
+ code: 37,
13
+ keyCode: 37,
14
+ };
15
+
16
+ const rightArrowKey = {
17
+ key: "ArrowRight",
18
+ code: 39,
19
+ keyCode: 39,
20
+ };
21
+
22
+ interface TypeButtonData {
23
+ id: string;
24
+ text: string;
25
+ }
26
+
27
+ const mapTabButton = (button: TypeButtonData) => (
28
+ <Tabs.Button key={button.id} id={button.id}>
29
+ {button.text}
30
+ </Tabs.Button>
31
+ );
32
+
33
+ interface TypeTestStatefulTabsProps {
34
+ children: React.ReactNode;
35
+ selectedId?: string;
36
+ onSelect?: (id: string) => void;
37
+ }
38
+
39
+ const TestStatefulTabs = ({
40
+ children,
41
+ selectedId = "1",
42
+ onSelect = jest.fn(),
43
+ }: TypeTestStatefulTabsProps) => {
44
+ const [state, setState] = React.useState({
45
+ selectedId,
46
+ });
47
+ return (
48
+ <Tabs
49
+ selectedId={state.selectedId}
50
+ onSelect={(id) => {
51
+ onSelect(id);
52
+ setState({
53
+ selectedId: id,
54
+ });
55
+ }}
56
+ >
57
+ {children}
58
+ </Tabs>
59
+ );
60
+ };
61
+
62
+ describe("Tabs", () => {
63
+ it("should render selected tab", () => {
64
+ render(
65
+ <TestStatefulTabs>
66
+ <Tabs.Button id="1">First</Tabs.Button>
67
+ <Tabs.Button id="2">Second</Tabs.Button>
68
+ </TestStatefulTabs>
69
+ );
70
+ const firstTab = screen.getByText(/first/i);
71
+ const secondTab = screen.getByText(/second/i);
72
+ expect(firstTab).toHaveAttribute("tabindex", "0");
73
+ expect(secondTab).toHaveAttribute("tabindex", "-1");
74
+ });
75
+
76
+ it("should work when tab buttons aren't direct children", () => {
77
+ render(
78
+ <TestStatefulTabs>
79
+ <Box border="2px red solid">
80
+ <Tabs.Button id="1">First</Tabs.Button>
81
+ </Box>
82
+ <Box border="2px green solid">
83
+ <Tabs.Button id="2">Second</Tabs.Button>
84
+ </Box>
85
+ </TestStatefulTabs>
86
+ );
87
+ const tabs = screen.getByDataQaLabel({
88
+ tabs: "",
89
+ });
90
+ const secondTab = screen.getByText(/second/i);
91
+ fireEvent.keyUp(tabs, rightArrowKey);
92
+ expect(secondTab).toHaveFocus();
93
+ });
94
+
95
+ it("should handle left arrow key", () => {
96
+ const onSelect = jest.fn();
97
+ render(
98
+ <TestStatefulTabs onSelect={onSelect}>
99
+ <Tabs.Button id="1">First</Tabs.Button>
100
+ <Tabs.Button id="2">Second</Tabs.Button>
101
+ <Tabs.Button id="3">Third</Tabs.Button>
102
+ </TestStatefulTabs>
103
+ );
104
+ const tabs = screen.getByDataQaLabel({
105
+ tabs: "",
106
+ });
107
+ const secondTab = screen.getByText(/second/i);
108
+ const thirdTab = screen.getByText(/third/i);
109
+ fireEvent.keyUp(tabs, leftArrowKey);
110
+ expect(onSelect).toHaveBeenCalledWith("3");
111
+ expect(thirdTab).toHaveFocus();
112
+ onSelect.mockReset();
113
+ fireEvent.keyUp(tabs, leftArrowKey);
114
+ expect(onSelect).toHaveBeenCalledWith("2");
115
+ expect(secondTab).toHaveFocus();
116
+ });
117
+
118
+ it("should handle right arrow key", () => {
119
+ const onSelect = jest.fn();
120
+ render(
121
+ <TestStatefulTabs onSelect={onSelect}>
122
+ <Tabs.Button id="1">First</Tabs.Button>
123
+ <Tabs.Button id="2">Second</Tabs.Button>
124
+ <Tabs.Button id="3">Third</Tabs.Button>
125
+ </TestStatefulTabs>
126
+ );
127
+ const tabs = screen.getByDataQaLabel({
128
+ tabs: "",
129
+ });
130
+ const firstTab = screen.getByText(/first/i);
131
+ const secondTab = screen.getByText(/second/i);
132
+ const thirdTab = screen.getByText(/third/i);
133
+ fireEvent.keyUp(tabs, rightArrowKey);
134
+ expect(onSelect).toHaveBeenCalledWith("2");
135
+ expect(secondTab).toHaveFocus();
136
+ onSelect.mockReset();
137
+ fireEvent.keyUp(tabs, rightArrowKey);
138
+ expect(onSelect).toHaveBeenCalledWith("3");
139
+ expect(thirdTab).toHaveFocus();
140
+ onSelect.mockReset();
141
+ fireEvent.keyUp(tabs, rightArrowKey);
142
+ expect(onSelect).toHaveBeenCalledWith("1");
143
+ expect(firstTab).toHaveFocus();
144
+ });
145
+
146
+ it("should handle dynamically adding tabs", () => {
147
+ const onSelect = jest.fn();
148
+ const initialButtons = [
149
+ {
150
+ id: "1",
151
+ text: "First",
152
+ },
153
+ {
154
+ id: "2",
155
+ text: "Second",
156
+ },
157
+ ];
158
+ const addButton = initialButtons.concat({
159
+ id: "3",
160
+ text: "Third",
161
+ });
162
+ const { rerender } = render(
163
+ <TestStatefulTabs onSelect={onSelect}>
164
+ {initialButtons.map(mapTabButton)}
165
+ </TestStatefulTabs>
166
+ );
167
+ const tabs = screen.getByDataQaLabel({
168
+ tabs: "",
169
+ });
170
+ const secondTab = screen.getByText(/second/i);
171
+ fireEvent.keyUp(tabs, rightArrowKey);
172
+ expect(onSelect).toHaveBeenCalledWith("2");
173
+ expect(secondTab).toHaveFocus();
174
+ rerender(
175
+ <TestStatefulTabs onSelect={onSelect}>
176
+ {addButton.map(mapTabButton)}
177
+ </TestStatefulTabs>
178
+ );
179
+ onSelect.mockReset();
180
+ fireEvent.keyUp(tabs, rightArrowKey);
181
+ const thirdTab = screen.getByText(/third/i);
182
+ expect(onSelect).toHaveBeenCalledWith("3");
183
+ expect(thirdTab).toHaveFocus();
184
+ });
185
+
186
+ it("should handle dynamically removing tabs", () => {
187
+ const onSelect = jest.fn();
188
+ const initialButtons = [
189
+ {
190
+ id: "1",
191
+ text: "First",
192
+ },
193
+ {
194
+ id: "2",
195
+ text: "Second",
196
+ },
197
+ {
198
+ id: "3",
199
+ text: "Third",
200
+ },
201
+ ];
202
+ const removeButton = initialButtons.slice(1);
203
+ const { rerender } = render(
204
+ <TestStatefulTabs onSelect={onSelect}>
205
+ {initialButtons.map(mapTabButton)}
206
+ </TestStatefulTabs>
207
+ );
208
+ const tabs = screen.getByDataQaLabel({
209
+ tabs: "",
210
+ });
211
+ const secondTab = screen.getByText(/second/i);
212
+ const thirdTab = screen.getByText(/third/i);
213
+ fireEvent.keyUp(tabs, rightArrowKey);
214
+ expect(onSelect).toHaveBeenCalledWith("2");
215
+ expect(secondTab).toHaveFocus();
216
+ rerender(
217
+ <TestStatefulTabs onSelect={onSelect}>
218
+ {removeButton.map(mapTabButton)}
219
+ </TestStatefulTabs>
220
+ );
221
+ onSelect.mockReset();
222
+ fireEvent.keyUp(tabs, rightArrowKey);
223
+ expect(onSelect).toHaveBeenCalledWith("3");
224
+ expect(thirdTab).toHaveFocus();
225
+ onSelect.mockReset();
226
+ fireEvent.keyUp(tabs, rightArrowKey);
227
+ expect(onSelect).toHaveBeenCalledWith("2");
228
+ expect(secondTab).toHaveFocus();
229
+ });
230
+
231
+ it("should handle dynamically changing tab ids", () => {
232
+ const onSelect = jest.fn();
233
+ const { rerender } = render(
234
+ <TestStatefulTabs onSelect={onSelect}>
235
+ <Tabs.Button id="1">First</Tabs.Button>
236
+ <Tabs.Button id="2">Second</Tabs.Button>
237
+ <Tabs.Button id="3">Third</Tabs.Button>
238
+ <Tabs.Button id="4">Fourth</Tabs.Button>
239
+ </TestStatefulTabs>
240
+ );
241
+
242
+ rerender(
243
+ <TestStatefulTabs onSelect={onSelect}>
244
+ <Tabs.Button id="1">First</Tabs.Button>
245
+ <Tabs.Button id="6">Second</Tabs.Button>
246
+ <Tabs.Button id="5">Third</Tabs.Button>
247
+ <Tabs.Button id="4">Fourth</Tabs.Button>
248
+ </TestStatefulTabs>
249
+ );
250
+
251
+ const tabs = screen.getByDataQaLabel({
252
+ tabs: "",
253
+ });
254
+
255
+ const secondTab = screen.getByText(/second/i);
256
+ const thirdTab = screen.getByText(/third/i);
257
+ const fourthTab = screen.getByText(/fourth/i);
258
+
259
+ fireEvent.keyUp(tabs, rightArrowKey);
260
+ expect(onSelect).toHaveBeenCalledWith("6");
261
+ expect(secondTab).toHaveFocus();
262
+ onSelect.mockReset();
263
+ fireEvent.keyUp(tabs, rightArrowKey);
264
+ expect(onSelect).toHaveBeenCalledWith("5");
265
+ expect(thirdTab).toHaveFocus();
266
+ onSelect.mockReset();
267
+ fireEvent.keyUp(tabs, rightArrowKey);
268
+ expect(onSelect).toHaveBeenCalledWith("4");
269
+ expect(fourthTab).toHaveFocus();
270
+ });
271
+ });
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import Tabs from "./Tabs";
2
+
3
+ export default Tabs;
4
+ export { Tabs };
5
+ export * from "./TabsTypes";
@@ -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
+ }