@purpurds/tabs 5.18.3 → 5.19.1
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/LICENSE.txt +1 -1
- package/dist/tabs.cjs.js +3 -3
- package/dist/tabs.cjs.js.map +1 -1
- package/dist/tabs.d.ts +4 -0
- package/dist/tabs.d.ts.map +1 -1
- package/dist/tabs.es.js +362 -356
- package/dist/tabs.es.js.map +1 -1
- package/package.json +6 -5
- package/src/tabs.stories.tsx +91 -0
- package/src/tabs.test.tsx +17 -0
- package/src/tabs.tsx +16 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purpurds/tabs",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.19.1",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"main": "./dist/tabs.cjs.js",
|
|
6
6
|
"types": "./dist/tabs.d.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"classnames": "~2.5.0",
|
|
18
18
|
"@radix-ui/react-tabs": "~1.0.4",
|
|
19
|
-
"@purpurds/
|
|
20
|
-
"@purpurds/
|
|
21
|
-
"@purpurds/paragraph": "5.
|
|
19
|
+
"@purpurds/icon": "5.19.1",
|
|
20
|
+
"@purpurds/tokens": "5.19.1",
|
|
21
|
+
"@purpurds/paragraph": "5.19.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@rushstack/eslint-patch": "~1.10.0",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"typescript": "^5.5.4",
|
|
43
43
|
"vite": "5.3.4",
|
|
44
44
|
"vitest": "~1.5.0",
|
|
45
|
-
"@purpurds/component-rig": "1.0.0"
|
|
45
|
+
"@purpurds/component-rig": "1.0.0",
|
|
46
|
+
"@purpurds/button": "5.19.1"
|
|
46
47
|
},
|
|
47
48
|
"scripts": {
|
|
48
49
|
"build:dev": "vite",
|
package/src/tabs.stories.tsx
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { useArgs } from "@storybook/preview-api";
|
|
3
4
|
|
|
4
5
|
import { Tabs } from "./tabs";
|
|
5
6
|
import { tabsVariants } from "./tabs.utils";
|
|
6
7
|
|
|
8
|
+
import { Button } from "@purpurds/button";
|
|
9
|
+
import "@purpurds/button/styles";
|
|
10
|
+
|
|
7
11
|
const meta = {
|
|
8
12
|
title: "Components/Tabs",
|
|
9
13
|
component: Tabs,
|
|
@@ -32,6 +36,26 @@ const meta = {
|
|
|
32
36
|
},
|
|
33
37
|
},
|
|
34
38
|
},
|
|
39
|
+
value: {
|
|
40
|
+
control: { type: "text" },
|
|
41
|
+
table: {
|
|
42
|
+
type: {
|
|
43
|
+
summary: "The controlled value of the tabs. Must be used in conjunction with onChange.",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
defaultValue: {
|
|
48
|
+
control: { type: "text" },
|
|
49
|
+
table: {
|
|
50
|
+
type: { summary: "The value of the tab to select by default, if uncontrolled" },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
onChange: {
|
|
54
|
+
table: {
|
|
55
|
+
type: { summary: "Event handler called when the value changes." },
|
|
56
|
+
disabled: true,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
35
59
|
fullWidth: {
|
|
36
60
|
table: { type: { summary: "boolean" } },
|
|
37
61
|
},
|
|
@@ -103,3 +127,70 @@ export const Showcase: Story = {
|
|
|
103
127
|
</div>
|
|
104
128
|
),
|
|
105
129
|
};
|
|
130
|
+
|
|
131
|
+
export const Controlled: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
variant: tabsVariants[0],
|
|
134
|
+
fullWidth: false,
|
|
135
|
+
children: [
|
|
136
|
+
<Tabs.Content
|
|
137
|
+
key="1"
|
|
138
|
+
tabId={`${tabId}-1`}
|
|
139
|
+
name={`${name}-1`}
|
|
140
|
+
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
141
|
+
>
|
|
142
|
+
<div>Content 1</div>
|
|
143
|
+
</Tabs.Content>,
|
|
144
|
+
<Tabs.Content
|
|
145
|
+
key="2"
|
|
146
|
+
tabId={`${tabId}-2`}
|
|
147
|
+
name={`${name}-2`}
|
|
148
|
+
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
149
|
+
>
|
|
150
|
+
<div>Content 2</div>
|
|
151
|
+
</Tabs.Content>,
|
|
152
|
+
<Tabs.Content
|
|
153
|
+
key="3"
|
|
154
|
+
tabId={`${tabId}-3`}
|
|
155
|
+
name={`${name}-3`}
|
|
156
|
+
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
157
|
+
>
|
|
158
|
+
<div>Content 3</div>
|
|
159
|
+
</Tabs.Content>,
|
|
160
|
+
],
|
|
161
|
+
negative: false,
|
|
162
|
+
value: "tab-2",
|
|
163
|
+
},
|
|
164
|
+
render: ({ children, ...args }) => {
|
|
165
|
+
const [{ localValue = args.value }, updateArgs] = useArgs(); // eslint-disable-line react-hooks/rules-of-hooks
|
|
166
|
+
const setValue = (value: string | undefined) => updateArgs({ value });
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<div
|
|
170
|
+
style={{
|
|
171
|
+
padding: "0 var(--purpur-spacing-250)",
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<Button onClick={() => setValue("tab-1")} variant="text" size="sm">
|
|
175
|
+
Click here for Tab 1
|
|
176
|
+
</Button>
|
|
177
|
+
</div>
|
|
178
|
+
<div
|
|
179
|
+
style={{
|
|
180
|
+
padding: "var(--purpur-spacing-250)",
|
|
181
|
+
background: args.negative
|
|
182
|
+
? "var(--purpur-color-purple-900)"
|
|
183
|
+
: args.variant === "contained"
|
|
184
|
+
? "var(--purpur-color-gray-50)"
|
|
185
|
+
: "transparent",
|
|
186
|
+
color: `var(--purpur-color-text-default${args.negative ? "-negative" : ""}`,
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
<Tabs {...args} value={localValue} onChange={(event) => setValue(event.detail.value)}>
|
|
190
|
+
{children}
|
|
191
|
+
</Tabs>
|
|
192
|
+
</div>
|
|
193
|
+
</>
|
|
194
|
+
);
|
|
195
|
+
},
|
|
196
|
+
};
|
package/src/tabs.test.tsx
CHANGED
|
@@ -148,4 +148,21 @@ describe("Tabs", () => {
|
|
|
148
148
|
);
|
|
149
149
|
}).toThrow("tabId must be unique");
|
|
150
150
|
});
|
|
151
|
+
|
|
152
|
+
it("should render controlled component", () => {
|
|
153
|
+
const testId = "tab-content";
|
|
154
|
+
render(
|
|
155
|
+
<Tabs value="tab-2">
|
|
156
|
+
<TabContent tabId="tab-1" name="Tab name 1" data-testid={`${testId}-1`}>
|
|
157
|
+
<div>Content</div>
|
|
158
|
+
</TabContent>
|
|
159
|
+
<TabContent tabId="tab-2" name="Tab name 2" data-testid={`${testId}-2`}>
|
|
160
|
+
<div>Content 2</div>
|
|
161
|
+
</TabContent>
|
|
162
|
+
</Tabs>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
expect(screen.queryByTestId(`${testId}-1`)?.getAttribute("data-state")).toBe("inactive");
|
|
166
|
+
expect(screen.queryByTestId(`${testId}-2`)?.getAttribute("data-state")).toBe("active");
|
|
167
|
+
});
|
|
151
168
|
});
|
package/src/tabs.tsx
CHANGED
|
@@ -13,6 +13,10 @@ type TabsProps = {
|
|
|
13
13
|
variant?: TabsVariant;
|
|
14
14
|
negative?: boolean;
|
|
15
15
|
fullWidth?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The controlled value of the tabs. Must be used in conjunction with onChange.
|
|
18
|
+
*/
|
|
19
|
+
value?: string;
|
|
16
20
|
/**
|
|
17
21
|
* Event handler called when the value changes.
|
|
18
22
|
* */
|
|
@@ -73,6 +77,7 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
73
77
|
className,
|
|
74
78
|
defaultValue,
|
|
75
79
|
"data-testid": dataTestId,
|
|
80
|
+
value,
|
|
76
81
|
...props
|
|
77
82
|
}) => {
|
|
78
83
|
const [scrollClasses, setScrollClasses] = useState<{ [key: string]: boolean }>({});
|
|
@@ -98,6 +103,12 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
98
103
|
return defaultIndex >= 0 ? defaultIndex : 0;
|
|
99
104
|
};
|
|
100
105
|
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (value) {
|
|
108
|
+
setActiveIndex(tabContentChildren.findIndex((child) => child.props.tabId === value));
|
|
109
|
+
}
|
|
110
|
+
}, [tabContentChildren, value]);
|
|
111
|
+
|
|
101
112
|
const [activeIndex, setActiveIndex] = useState(getDefaultTabIndex);
|
|
102
113
|
|
|
103
114
|
if (new Set(tabIds).size !== tabIds.length) {
|
|
@@ -118,11 +129,11 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
118
129
|
setSelectedTriggerWidth(activeTabElement?.getBoundingClientRect().width || 0);
|
|
119
130
|
};
|
|
120
131
|
|
|
121
|
-
const handleTabChange = (
|
|
122
|
-
if (isLineVariant) {
|
|
123
|
-
setActiveIndex(tabContentChildren.findIndex((child) => child.props.tabId ===
|
|
132
|
+
const handleTabChange = (_value: string) => {
|
|
133
|
+
if (isLineVariant && !value) {
|
|
134
|
+
setActiveIndex(tabContentChildren.findIndex((child) => child.props.tabId === _value));
|
|
124
135
|
}
|
|
125
|
-
onChange?.(createTabChangeDetailEvent(
|
|
136
|
+
onChange?.(createTabChangeDetailEvent(_value));
|
|
126
137
|
};
|
|
127
138
|
|
|
128
139
|
const handleScrollButtonClick = (side: "left" | "right") => {
|
|
@@ -207,6 +218,7 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
207
218
|
onValueChange={handleTabChange}
|
|
208
219
|
data-testid={dataTestId}
|
|
209
220
|
className={classNames}
|
|
221
|
+
value={value}
|
|
210
222
|
{...props}
|
|
211
223
|
>
|
|
212
224
|
<div className={cx(`${rootClassName}__container`)}>
|