@stackshift-ui/button 6.0.11-beta.2 → 6.1.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 -6
- package/src/button.test.tsx +32 -2
- package/src/button.tsx +51 -125
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackshift-ui/button",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0-beta.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,15 +27,17 @@
|
|
|
27
27
|
"typescript": "^5.6.2",
|
|
28
28
|
"vite-tsconfig-paths": "^5.0.1",
|
|
29
29
|
"vitest": "^2.1.1",
|
|
30
|
-
"@stackshift-ui/eslint-config": "6.0.10
|
|
31
|
-
"@stackshift-ui/typescript-config": "6.0.10
|
|
30
|
+
"@stackshift-ui/eslint-config": "6.0.10",
|
|
31
|
+
"@stackshift-ui/typescript-config": "6.0.10"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
35
|
+
"class-variance-authority": "^0.7.1",
|
|
34
36
|
"classnames": "^2.5.1",
|
|
35
37
|
"react-icons": "^5.3.0",
|
|
36
|
-
"@stackshift-ui/link": "6.0.
|
|
37
|
-
"@stackshift-ui/
|
|
38
|
-
"@stackshift-ui/
|
|
38
|
+
"@stackshift-ui/link": "6.0.12-beta.0",
|
|
39
|
+
"@stackshift-ui/system": "6.1.0-beta.0",
|
|
40
|
+
"@stackshift-ui/scripts": "6.1.0-beta.0"
|
|
39
41
|
},
|
|
40
42
|
"peerDependencies": {
|
|
41
43
|
"@types/react": "16.8 - 19",
|
package/src/button.test.tsx
CHANGED
|
@@ -7,7 +7,37 @@ describe.concurrent("button", () => {
|
|
|
7
7
|
|
|
8
8
|
test("Common: Button - test if renders without errors", ({ expect }) => {
|
|
9
9
|
const clx = "button-class";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
const { unmount } = render(<Button className={clx}>Test Button</Button>);
|
|
12
|
+
|
|
13
|
+
const button = screen.getByRole("button", { name: "Test Button" });
|
|
14
|
+
expect(button.classList).toContain(clx);
|
|
15
|
+
expect(button.textContent).toBe("Test Button");
|
|
16
|
+
unmount();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("Common: Button - test if renders as child", ({ expect }) => {
|
|
20
|
+
const { unmount } = render(
|
|
21
|
+
<Button asChild>
|
|
22
|
+
<a href="#" target="_blank" rel="noopener noreferrer">
|
|
23
|
+
External Link
|
|
24
|
+
</a>
|
|
25
|
+
</Button>,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const link = screen.getByRole("link", { name: "External Link" });
|
|
29
|
+
expect(link.tagName.toLowerCase()).toBe("a");
|
|
30
|
+
expect(link.getAttribute("href")).toBe("#");
|
|
31
|
+
expect(link.getAttribute("target")).toBe("_blank");
|
|
32
|
+
expect(link.getAttribute("rel")).toBe("noopener noreferrer");
|
|
33
|
+
unmount();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("Common: Button - test if renders with correct variant", ({ expect }) => {
|
|
37
|
+
const { unmount } = render(<Button variant="destructive">Destructive Button</Button>);
|
|
38
|
+
|
|
39
|
+
const button = screen.getByRole("button", { name: "Destructive Button" });
|
|
40
|
+
expect(button.classList).toContain("bg-destructive");
|
|
41
|
+
unmount();
|
|
12
42
|
});
|
|
13
43
|
});
|
package/src/button.tsx
CHANGED
|
@@ -1,135 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from "react";
|
|
5
|
-
import { FaSpinner } from "react-icons/fa";
|
|
6
|
-
import { extractLink } from "./helper";
|
|
7
|
-
import { type LabeledRoute, type StyleVariants } from "./types";
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
| "outline"
|
|
11
|
-
| "ghost"
|
|
12
|
-
| "link"
|
|
13
|
-
| "custom"
|
|
14
|
-
| "solid"
|
|
15
|
-
| "addToWishlist"
|
|
16
|
-
| "unstyled"
|
|
17
|
-
| "swiper_pagination"
|
|
18
|
-
| "tab";
|
|
19
|
-
type TextSize = "xs" | "sm" | "md" | "lg";
|
|
20
|
-
type RadiusSize = "none" | "sm" | "md" | "base" | "lg" | "xl" | "2xl" | "full";
|
|
21
|
-
|
|
22
|
-
interface BaseType {
|
|
23
|
-
children?: ReactNode;
|
|
24
|
-
variant?: Variant;
|
|
25
|
-
radius?: RadiusSize;
|
|
26
|
-
size?: TextSize;
|
|
27
|
-
ariaLabel?: string;
|
|
28
|
-
isActive?: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface LinkProps extends BaseType, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "type"> {
|
|
32
|
-
link: LabeledRoute;
|
|
33
|
-
as: "link";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface ButtonProps extends BaseType, ButtonHTMLAttributes<HTMLButtonElement> {
|
|
37
|
-
as?: "button";
|
|
38
|
-
loading?: boolean;
|
|
39
|
-
disabled?: boolean;
|
|
40
|
-
loadingComponent?: React.ReactNode;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type Props = ButtonProps | LinkProps;
|
|
5
|
+
import { cn, DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
|
|
44
6
|
|
|
45
7
|
const displayName = "Button";
|
|
46
8
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const linkType = `transition-200 text-primary hover:text-primary/50 underline ${buttonRadius} ${cn(buttonSize, "px-0 py-0")}`;
|
|
81
|
-
const unstyled = ``;
|
|
82
|
-
const swiper_pagination = `mr-1 ${isActive ? "bg-primary" : "bg-gray-200"} rounded-full p-1 focus:outline-none`;
|
|
83
|
-
const tab = `mx-auto mb-1 w-auto px-4 py-2 rounded duration-200 transition focus:outline-none font-bold ${
|
|
84
|
-
isActive
|
|
85
|
-
? "bg-gray-50 text-primary shadow"
|
|
86
|
-
: "text-gray-700 hover:bg-secondary/50 hover:text-primary hover:shadow"
|
|
87
|
-
}`;
|
|
88
|
-
const addToWishlist = `${commonStyles} ${buttonRadius} ${buttonSize} ml-auto sm:ml-0 flex-shrink-0 inline-flex items-center justify-center w-full rounded-md border hover:border-primary`;
|
|
89
|
-
|
|
90
|
-
const variants: StyleVariants<Variant> = {
|
|
91
|
-
outline,
|
|
92
|
-
ghost,
|
|
93
|
-
link: linkType,
|
|
94
|
-
custom,
|
|
95
|
-
solid,
|
|
96
|
-
addToWishlist,
|
|
97
|
-
unstyled,
|
|
98
|
-
swiper_pagination,
|
|
99
|
-
tab,
|
|
100
|
-
};
|
|
9
|
+
const buttonVariants = cva(
|
|
10
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
unstyled:
|
|
15
|
+
"bg-transparent p-0 border-none shadow-none hover:bg-transparent ring-0 outline-none text-inherit",
|
|
16
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
17
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
18
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
19
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
20
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-10 px-4 py-2",
|
|
25
|
+
sm: "h-9 rounded-md px-3",
|
|
26
|
+
lg: "h-11 rounded-md px-8",
|
|
27
|
+
icon: "h-10 w-10",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export interface ButtonProps
|
|
38
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
+
VariantProps<typeof buttonVariants> {
|
|
40
|
+
asChild?: boolean;
|
|
41
|
+
}
|
|
101
42
|
|
|
102
|
-
|
|
43
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
+
const { [displayName]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
103
46
|
|
|
104
|
-
|
|
105
|
-
const { link, ...rest } = props as LinkProps;
|
|
47
|
+
const Comp = asChild ? Slot : "button";
|
|
106
48
|
|
|
107
49
|
return (
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{...rest}>
|
|
115
|
-
{children}
|
|
116
|
-
</Link>
|
|
50
|
+
<Component
|
|
51
|
+
as={Comp}
|
|
52
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
53
|
+
ref={ref}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
117
56
|
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
Button.displayName = displayName;
|
|
121
60
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Component
|
|
126
|
-
onClick={onClick}
|
|
127
|
-
disabled={disabled ?? loading}
|
|
128
|
-
className={cn(variantClass, className)}
|
|
129
|
-
aria-label={ariaLabel}
|
|
130
|
-
data-testid={displayName.toLowerCase()}
|
|
131
|
-
type={type}>
|
|
132
|
-
{loading ? Loader : children}
|
|
133
|
-
</Component>
|
|
134
|
-
);
|
|
135
|
-
}
|
|
61
|
+
export { Button, buttonVariants };
|
package/src/index.ts
CHANGED