@noya-app/noya-designsystem 0.1.92 → 0.1.94
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +16 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +11484 -11073
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12764 -12354
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/DropdownMenu.tsx +14 -1
- package/src/components/Toast.tsx +2 -2
- package/src/components/__tests__/DropdownMenu.test.ts +19 -0
- package/src/components/__tests__/Toast.test.tsx +138 -0
- package/src/index.css +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noya-app/noya-designsystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.94",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"lint": "eslint . --max-warnings 0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@base-ui/react": "1.
|
|
28
|
+
"@base-ui/react": "1.7.0",
|
|
29
29
|
"@dnd-kit/core": "6.3.1",
|
|
30
30
|
"@dnd-kit/sortable": "10.0.0",
|
|
31
31
|
"@floating-ui/react": "^0.27.0",
|
|
@@ -236,12 +236,16 @@ function MenuViewport<T extends string>({
|
|
|
236
236
|
}
|
|
237
237
|
return null;
|
|
238
238
|
|
|
239
|
-
default:
|
|
239
|
+
default: {
|
|
240
|
+
const accessibilityProps = getCheckedMenuItemAccessibilityProps(
|
|
241
|
+
item.checked
|
|
242
|
+
);
|
|
240
243
|
return (
|
|
241
244
|
<div key={item.value} className="n-px-1">
|
|
242
245
|
<Menu.Item
|
|
243
246
|
disabled={item.disabled}
|
|
244
247
|
className={itemStyle}
|
|
248
|
+
{...accessibilityProps}
|
|
245
249
|
onClick={() => {
|
|
246
250
|
if (onSelect && item.value) {
|
|
247
251
|
// Delay onSelect to allow menu to close and focus to settle
|
|
@@ -262,12 +266,21 @@ function MenuViewport<T extends string>({
|
|
|
262
266
|
</Menu.Item>
|
|
263
267
|
</div>
|
|
264
268
|
);
|
|
269
|
+
}
|
|
265
270
|
}
|
|
266
271
|
})}
|
|
267
272
|
</>
|
|
268
273
|
);
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
export function getCheckedMenuItemAccessibilityProps(
|
|
277
|
+
checked: boolean | undefined
|
|
278
|
+
) {
|
|
279
|
+
return checked === undefined
|
|
280
|
+
? {}
|
|
281
|
+
: ({ role: "menuitemcheckbox", "aria-checked": checked } as const);
|
|
282
|
+
}
|
|
283
|
+
|
|
271
284
|
const DropdownMenuRoot = forwardRefGeneric(function DropdownMenuRoot<
|
|
272
285
|
T extends string,
|
|
273
286
|
>(props: DropdownMenuProps<T>, forwardedRef: ForwardedRef<HTMLElement>) {
|
package/src/components/Toast.tsx
CHANGED
|
@@ -39,7 +39,7 @@ function ToastContent({ toast }: { toast: ToastObject }) {
|
|
|
39
39
|
return (
|
|
40
40
|
<ToastPrimitive.Root
|
|
41
41
|
toast={toast}
|
|
42
|
-
className="n-relative n-flex n-items-start n-gap-2 n-rounded n-text-sm n-bg-popover-background n-shadow-popover n-text-text-muted n-px-3 n-py-2.5 n-pr-10"
|
|
42
|
+
className="n-pointer-events-auto n-relative n-flex n-items-start n-gap-2 n-rounded n-text-sm n-bg-popover-background n-shadow-popover n-text-text-muted n-px-3 n-py-2.5 n-pr-10"
|
|
43
43
|
>
|
|
44
44
|
<div className="n-flex n-flex-col n-gap-0.5 n-min-w-0">
|
|
45
45
|
{toast.data?.title && (
|
|
@@ -87,7 +87,7 @@ function ToastProviderInner({ children }: { children: React.ReactNode }) {
|
|
|
87
87
|
return (
|
|
88
88
|
<ToastContext.Provider value={{ showToast }}>
|
|
89
89
|
{children}
|
|
90
|
-
<ToastPrimitive.Viewport className="n-fixed n-bottom-0 n-right-0 n-flex n-flex-col n-p-5 n-gap-2.5 n-min-w-[200px] n-max-w-[100vw] n-m-0 n-list-none n-z-[2147483647] n-outline-none">
|
|
90
|
+
<ToastPrimitive.Viewport className="n-pointer-events-none n-fixed n-bottom-0 n-right-0 n-flex n-flex-col n-p-5 n-gap-2.5 n-min-w-[200px] n-max-w-[100vw] n-m-0 n-list-none n-z-[2147483647] n-outline-none">
|
|
91
91
|
{toastManager.toasts.map((toast) => (
|
|
92
92
|
<ToastContent key={toast.id} toast={toast} />
|
|
93
93
|
))}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { getCheckedMenuItemAccessibilityProps } from "../DropdownMenu";
|
|
3
|
+
|
|
4
|
+
describe("DropdownMenu checked items", () => {
|
|
5
|
+
test("uses checkbox menu semantics when checked state is provided", () => {
|
|
6
|
+
expect(getCheckedMenuItemAccessibilityProps(false)).toEqual({
|
|
7
|
+
role: "menuitemcheckbox",
|
|
8
|
+
"aria-checked": false,
|
|
9
|
+
});
|
|
10
|
+
expect(getCheckedMenuItemAccessibilityProps(true)).toEqual({
|
|
11
|
+
role: "menuitemcheckbox",
|
|
12
|
+
"aria-checked": true,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("leaves ordinary menu items unchanged", () => {
|
|
17
|
+
expect(getCheckedMenuItemAccessibilityProps(undefined)).toEqual({});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
const actualIcons = await import("@radix-ui/react-icons");
|
|
5
|
+
|
|
6
|
+
mock.module("@radix-ui/react-icons", () => ({
|
|
7
|
+
...actualIcons,
|
|
8
|
+
Cross1Icon: (props: React.SVGProps<SVGSVGElement>) => (
|
|
9
|
+
<svg {...props} data-testid="cross-icon" />
|
|
10
|
+
),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const { act, cleanup, fireEvent, render, waitFor } = await import(
|
|
14
|
+
"@testing-library/react"
|
|
15
|
+
);
|
|
16
|
+
const { ToastProvider, useToast } = await import("../Toast");
|
|
17
|
+
|
|
18
|
+
afterEach(cleanup);
|
|
19
|
+
|
|
20
|
+
function ToastFixture({ onAction }: { onAction?: () => void }) {
|
|
21
|
+
const { showToast } = useToast();
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<button
|
|
25
|
+
onClick={() =>
|
|
26
|
+
showToast({
|
|
27
|
+
title: "Saved",
|
|
28
|
+
content: "Your changes were saved.",
|
|
29
|
+
action: onAction ? (
|
|
30
|
+
<button onClick={onAction}>Undo</button>
|
|
31
|
+
) : undefined,
|
|
32
|
+
timeout: 60_000,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
>
|
|
36
|
+
Show toast
|
|
37
|
+
</button>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function renderToast(onAction?: () => void) {
|
|
42
|
+
const view = render(
|
|
43
|
+
<ToastProvider>
|
|
44
|
+
<ToastFixture onAction={onAction} />
|
|
45
|
+
</ToastProvider>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
fireEvent.click(view.getByRole("button", { name: "Show toast" }));
|
|
49
|
+
|
|
50
|
+
return view;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("ToastProvider pointer events", () => {
|
|
54
|
+
test("keeps the viewport pointer-transparent when no toast exists", () => {
|
|
55
|
+
const view = render(
|
|
56
|
+
<ToastProvider>
|
|
57
|
+
<button>Underlying control</button>
|
|
58
|
+
</ToastProvider>
|
|
59
|
+
);
|
|
60
|
+
const viewport = view.getByRole("region", { name: "Notifications" });
|
|
61
|
+
|
|
62
|
+
expect(viewport.classList.contains("n-pointer-events-none")).toBe(true);
|
|
63
|
+
expect(view.queryByRole("dialog")).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("makes toast roots interactive inside the transparent viewport", () => {
|
|
67
|
+
const view = renderToast();
|
|
68
|
+
const viewport = view.getByRole("region", { name: "Notifications" });
|
|
69
|
+
const toast = view.getByRole("dialog");
|
|
70
|
+
|
|
71
|
+
expect(viewport.classList.contains("n-pointer-events-none")).toBe(true);
|
|
72
|
+
expect(toast.classList.contains("n-pointer-events-auto")).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("ToastProvider interactions", () => {
|
|
77
|
+
test("preserves action and close button interactions", async () => {
|
|
78
|
+
const onAction = mock(() => {});
|
|
79
|
+
const view = renderToast(onAction);
|
|
80
|
+
|
|
81
|
+
fireEvent.click(view.getByRole("button", { name: "Undo" }));
|
|
82
|
+
expect(onAction).toHaveBeenCalledTimes(1);
|
|
83
|
+
|
|
84
|
+
fireEvent.click(view.getByLabelText("Close"));
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(view.queryByText("Your changes were saved.")).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("preserves keyboard dismissal", async () => {
|
|
91
|
+
const view = renderToast();
|
|
92
|
+
const toast = view.getByRole("dialog");
|
|
93
|
+
|
|
94
|
+
act(() => toast.focus());
|
|
95
|
+
fireEvent.keyDown(toast, { key: "Escape" });
|
|
96
|
+
|
|
97
|
+
await waitFor(() => {
|
|
98
|
+
expect(view.queryByText("Your changes were saved.")).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("preserves swipe dismissal", async () => {
|
|
103
|
+
const view = renderToast();
|
|
104
|
+
const toast = view.getByRole("dialog");
|
|
105
|
+
|
|
106
|
+
fireEvent.pointerDown(toast, {
|
|
107
|
+
button: 0,
|
|
108
|
+
clientX: 0,
|
|
109
|
+
clientY: 0,
|
|
110
|
+
pointerId: 1,
|
|
111
|
+
pointerType: "touch",
|
|
112
|
+
});
|
|
113
|
+
fireEvent.pointerMove(toast, {
|
|
114
|
+
clientX: 1,
|
|
115
|
+
clientY: 0,
|
|
116
|
+
pointerId: 1,
|
|
117
|
+
pointerType: "touch",
|
|
118
|
+
});
|
|
119
|
+
fireEvent.pointerMove(toast, {
|
|
120
|
+
clientX: 60,
|
|
121
|
+
clientY: 0,
|
|
122
|
+
movementX: 59,
|
|
123
|
+
movementY: 0,
|
|
124
|
+
pointerId: 1,
|
|
125
|
+
pointerType: "touch",
|
|
126
|
+
});
|
|
127
|
+
fireEvent.pointerUp(toast, {
|
|
128
|
+
clientX: 60,
|
|
129
|
+
clientY: 0,
|
|
130
|
+
pointerId: 1,
|
|
131
|
+
pointerType: "touch",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(view.queryByText("Your changes were saved.")).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
package/src/index.css
CHANGED
|
@@ -291,7 +291,8 @@
|
|
|
291
291
|
|
|
292
292
|
/* Direct child pre blocks */
|
|
293
293
|
.n-prose pre {
|
|
294
|
-
|
|
294
|
+
color: var(--n-text);
|
|
295
|
+
background-color: var(--n-code-background);
|
|
295
296
|
border-width: 1px;
|
|
296
297
|
border-style: solid;
|
|
297
298
|
border-color: var(--n-divider);
|