@stackshift-ui/dialog 1.0.0-beta.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/LICENSE +373 -0
- package/README.md +24 -0
- package/dist/chunk-2VQY25ZS.mjs +1 -0
- package/dist/chunk-3EYN2AKU.mjs +1 -0
- package/dist/dialog.d.ts +45 -0
- package/dist/dialog.js +2 -0
- package/dist/dialog.mjs +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.mjs +2 -0
- package/dist/setupTests.d.ts +2 -0
- package/dist/setupTests.js +51 -0
- package/dist/setupTests.mjs +51 -0
- package/package.json +64 -0
- package/src/dialog.test.tsx +88 -0
- package/src/dialog.tsx +172 -0
- package/src/index.ts +4 -0
- package/src/setupTests.ts +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stackshift-ui/dialog",
|
|
3
|
+
"description": "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.",
|
|
4
|
+
"version": "1.0.0-beta.1",
|
|
5
|
+
"private": false,
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"author": "WebriQ <info@webriq.com>",
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
17
|
+
"@testing-library/react": "^16.0.1",
|
|
18
|
+
"@testing-library/user-event": "^14.6.1",
|
|
19
|
+
"@types/node": "^22.7.0",
|
|
20
|
+
"@types/react": "^18.3.9",
|
|
21
|
+
"@types/react-dom": "^18.3.0",
|
|
22
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
23
|
+
"@vitest/coverage-v8": "^2.1.1",
|
|
24
|
+
"esbuild-plugin-rdi": "^0.0.0",
|
|
25
|
+
"esbuild-plugin-react18": "^0.2.5",
|
|
26
|
+
"esbuild-plugin-react18-css": "^0.0.4",
|
|
27
|
+
"jsdom": "^25.0.1",
|
|
28
|
+
"react": "^18.3.1",
|
|
29
|
+
"react-dom": "^18.3.1",
|
|
30
|
+
"tsup": "^8.3.0",
|
|
31
|
+
"typescript": "^5.6.2",
|
|
32
|
+
"vite-tsconfig-paths": "^5.0.1",
|
|
33
|
+
"vitest": "^2.1.1",
|
|
34
|
+
"@stackshift-ui/eslint-config": "6.0.10",
|
|
35
|
+
"@stackshift-ui/typescript-config": "6.0.10"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@radix-ui/react-dialog": "^1.1.14",
|
|
39
|
+
"classnames": "^2.5.1",
|
|
40
|
+
"lucide-react": "^0.468.0",
|
|
41
|
+
"@stackshift-ui/scripts": "6.1.0-beta.0",
|
|
42
|
+
"@stackshift-ui/system": "6.1.0-beta.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@stackshift-ui/system": ">=6.1.0-beta.0",
|
|
46
|
+
"@types/react": "16.8 - 19",
|
|
47
|
+
"next": "10 - 14",
|
|
48
|
+
"react": "16.8 - 19",
|
|
49
|
+
"react-dom": "16.8 - 19"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"next": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup && tsc -p tsconfig-build.json",
|
|
58
|
+
"clean": "rm -rf dist",
|
|
59
|
+
"dev": "tsup --watch && tsc -p tsconfig-build.json -w",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"lint": "eslint src/",
|
|
62
|
+
"test": "vitest run --coverage"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { afterEach, describe, test } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogDescription,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
DialogTrigger,
|
|
11
|
+
} from "./dialog";
|
|
12
|
+
|
|
13
|
+
describe.concurrent("dialog", () => {
|
|
14
|
+
afterEach(cleanup);
|
|
15
|
+
|
|
16
|
+
test("Dummy test - test if renders without errors", async ({ expect }) => {
|
|
17
|
+
const clx = "my-class";
|
|
18
|
+
|
|
19
|
+
const user = userEvent.setup();
|
|
20
|
+
|
|
21
|
+
const { unmount } = render(
|
|
22
|
+
<Dialog>
|
|
23
|
+
<DialogTrigger asChild data-testid="dialog-trigger-1">
|
|
24
|
+
<button>Open Dialog</button>
|
|
25
|
+
</DialogTrigger>
|
|
26
|
+
<DialogContent data-testid="dialog-container-1" className={clx}>
|
|
27
|
+
<DialogHeader>
|
|
28
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
29
|
+
<DialogDescription>Dialog Description</DialogDescription>
|
|
30
|
+
</DialogHeader>
|
|
31
|
+
</DialogContent>
|
|
32
|
+
</Dialog>,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const trigger = screen.getByTestId("dialog-trigger-1");
|
|
36
|
+
expect(trigger).toBeInTheDocument();
|
|
37
|
+
|
|
38
|
+
await user.click(trigger);
|
|
39
|
+
|
|
40
|
+
const dialog = await screen.findByTestId("dialog-container-1");
|
|
41
|
+
|
|
42
|
+
expect(dialog).toBeInTheDocument();
|
|
43
|
+
|
|
44
|
+
unmount();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("Common: Dialog - test if closes when clicking outside", async ({ expect }) => {
|
|
48
|
+
const user = userEvent.setup();
|
|
49
|
+
const { unmount } = render(
|
|
50
|
+
<>
|
|
51
|
+
<button data-testid="outside-button">Outside Button</button>
|
|
52
|
+
<Dialog>
|
|
53
|
+
<DialogTrigger asChild data-testid="dialog-trigger">
|
|
54
|
+
<button>Open Dialog</button>
|
|
55
|
+
</DialogTrigger>
|
|
56
|
+
<DialogContent data-testid="dialog-container">
|
|
57
|
+
<DialogHeader>
|
|
58
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
59
|
+
<DialogDescription>Dialog Description</DialogDescription>
|
|
60
|
+
</DialogHeader>
|
|
61
|
+
</DialogContent>
|
|
62
|
+
</Dialog>
|
|
63
|
+
</>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const outsideButton = screen.getByTestId("outside-button");
|
|
67
|
+
expect(outsideButton).toBeInTheDocument();
|
|
68
|
+
|
|
69
|
+
const trigger = screen.getByTestId("dialog-trigger");
|
|
70
|
+
expect(trigger).toBeInTheDocument();
|
|
71
|
+
|
|
72
|
+
await user.click(trigger);
|
|
73
|
+
|
|
74
|
+
await waitFor(() => {
|
|
75
|
+
const dialog = screen.getByTestId("dialog-container");
|
|
76
|
+
expect(dialog).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await user.click(outsideButton);
|
|
80
|
+
|
|
81
|
+
waitFor(() => {
|
|
82
|
+
const dialog = screen.queryByTestId("dialog-container");
|
|
83
|
+
expect(dialog).not.toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
unmount();
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/dialog.tsx
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
|
+
import { XIcon } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn, DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
|
|
8
|
+
|
|
9
|
+
const displayName = "Dialog";
|
|
10
|
+
const displayNameTrigger = "DialogTrigger";
|
|
11
|
+
const displayNamePortal = "DialogPortal";
|
|
12
|
+
const displayNameClose = "DialogClose";
|
|
13
|
+
const displayNameOverlay = "DialogOverlay";
|
|
14
|
+
const displayNameContent = "DialogContent";
|
|
15
|
+
const displayNameHeader = "DialogHeader";
|
|
16
|
+
const displayNameFooter = "DialogFooter";
|
|
17
|
+
const displayNameTitle = "DialogTitle";
|
|
18
|
+
const displayNameDescription = "DialogDescription";
|
|
19
|
+
|
|
20
|
+
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
21
|
+
const { [displayName]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
22
|
+
|
|
23
|
+
return <Component as={DialogPrimitive.Root} data-slot="dialog" {...props} />;
|
|
24
|
+
}
|
|
25
|
+
Dialog.displayName = displayName;
|
|
26
|
+
|
|
27
|
+
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
28
|
+
const { [displayNameTrigger]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
29
|
+
|
|
30
|
+
return <Component as={DialogPrimitive.Trigger} data-slot="dialog-trigger" {...props} />;
|
|
31
|
+
}
|
|
32
|
+
DialogTrigger.displayName = displayNameTrigger;
|
|
33
|
+
|
|
34
|
+
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
35
|
+
const { [displayNamePortal]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
36
|
+
|
|
37
|
+
return <Component as={DialogPrimitive.Portal} data-slot="dialog-portal" {...props} />;
|
|
38
|
+
}
|
|
39
|
+
DialogPortal.displayName = displayNamePortal;
|
|
40
|
+
|
|
41
|
+
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
42
|
+
const { [displayNameClose]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
43
|
+
|
|
44
|
+
return <Component as={DialogPrimitive.Close} data-slot="dialog-close" {...props} />;
|
|
45
|
+
}
|
|
46
|
+
DialogClose.displayName = displayNameClose;
|
|
47
|
+
|
|
48
|
+
function DialogOverlay({
|
|
49
|
+
className,
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
52
|
+
const { [displayNameOverlay]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Component
|
|
56
|
+
as={DialogPrimitive.Overlay}
|
|
57
|
+
data-slot="dialog-overlay"
|
|
58
|
+
className={cn(
|
|
59
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
60
|
+
className,
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
DialogOverlay.displayName = displayNameOverlay;
|
|
67
|
+
|
|
68
|
+
function DialogContent({
|
|
69
|
+
className,
|
|
70
|
+
children,
|
|
71
|
+
showCloseButton = true,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
74
|
+
showCloseButton?: boolean;
|
|
75
|
+
}) {
|
|
76
|
+
const { [displayNameContent]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<DialogPortal data-slot="dialog-portal">
|
|
80
|
+
<DialogOverlay />
|
|
81
|
+
<Component
|
|
82
|
+
as={DialogPrimitive.Content}
|
|
83
|
+
data-slot="dialog-content"
|
|
84
|
+
className={cn(
|
|
85
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
{...props}>
|
|
89
|
+
{children}
|
|
90
|
+
{showCloseButton && (
|
|
91
|
+
<DialogPrimitive.Close
|
|
92
|
+
data-slot="dialog-close"
|
|
93
|
+
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
|
94
|
+
<XIcon />
|
|
95
|
+
<span className="sr-only">Close</span>
|
|
96
|
+
</DialogPrimitive.Close>
|
|
97
|
+
)}
|
|
98
|
+
</Component>
|
|
99
|
+
</DialogPortal>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
DialogContent.displayName = displayNameContent;
|
|
103
|
+
|
|
104
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
105
|
+
const { [displayNameHeader]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Component
|
|
109
|
+
data-slot="dialog-header"
|
|
110
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
DialogHeader.displayName = displayNameHeader;
|
|
116
|
+
|
|
117
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
118
|
+
const { [displayNameFooter]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Component
|
|
122
|
+
data-slot="dialog-footer"
|
|
123
|
+
className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
DialogFooter.displayName = displayNameFooter;
|
|
129
|
+
|
|
130
|
+
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
131
|
+
const { [displayNameTitle]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<Component
|
|
135
|
+
as={DialogPrimitive.Title}
|
|
136
|
+
data-slot="dialog-title"
|
|
137
|
+
className={cn("text-lg leading-none font-semibold", className)}
|
|
138
|
+
{...props}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
DialogTitle.displayName = displayNameTitle;
|
|
143
|
+
|
|
144
|
+
function DialogDescription({
|
|
145
|
+
className,
|
|
146
|
+
...props
|
|
147
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
148
|
+
const { [displayNameDescription]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<Component
|
|
152
|
+
as={DialogPrimitive.Description}
|
|
153
|
+
data-slot="dialog-description"
|
|
154
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
DialogDescription.displayName = displayNameDescription;
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
Dialog,
|
|
163
|
+
DialogClose,
|
|
164
|
+
DialogContent,
|
|
165
|
+
DialogDescription,
|
|
166
|
+
DialogFooter,
|
|
167
|
+
DialogHeader,
|
|
168
|
+
DialogOverlay,
|
|
169
|
+
DialogPortal,
|
|
170
|
+
DialogTitle,
|
|
171
|
+
DialogTrigger,
|
|
172
|
+
};
|
package/src/index.ts
ADDED