@bug-on/md3-react 3.0.1 → 3.0.2
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 +41 -42
- package/CHANGELOG.md +10 -0
- package/dist/index.d.mts +54 -3
- package/dist/index.d.ts +54 -3
- package/dist/index.js +764 -473
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +710 -421
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +8 -0
- package/src/ui/navigation-bar.test.tsx +111 -0
- package/src/ui/navigation-bar.tsx +448 -0
- package/src/ui/navigation-rail.test.tsx +5 -4
- package/src/ui/navigation-rail.tsx +20 -21
- package/src/ui/scroll-area.tsx +4 -0
- package/src/ui/shared/constants.ts +1 -1
- package/test_output.txt +0 -164
- package/test_output_v2.txt +0 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
-
import { AnimatePresence, domMax, LazyMotion, m } from "motion/react";
|
|
2
|
+
import { AnimatePresence, domMax, LazyMotion, m, type Transition } from "motion/react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
import { cn } from "../lib/utils";
|
|
@@ -15,7 +15,7 @@ import { TouchTarget } from "./shared/touch-target";
|
|
|
15
15
|
// Types & Constants
|
|
16
16
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
17
|
|
|
18
|
-
export type NavigationRailVariant = "collapsed" | "expanded" | "modal";
|
|
18
|
+
export type NavigationRailVariant = "collapsed" | "expanded" | "modal" | "xr";
|
|
19
19
|
export type NavigationRailLabelVisibility = "labeled" | "auto" | "unlabeled";
|
|
20
20
|
|
|
21
21
|
export interface NavigationRailItemProps {
|
|
@@ -34,11 +34,12 @@ export interface NavigationRailProps {
|
|
|
34
34
|
labelVisibility?: NavigationRailLabelVisibility;
|
|
35
35
|
header?: React.ReactNode;
|
|
36
36
|
fab?: React.ReactNode;
|
|
37
|
+
fabPlacement?: "contained" | "spatialized";
|
|
37
38
|
footer?: React.ReactNode;
|
|
38
39
|
narrow?: boolean;
|
|
39
40
|
open?: boolean;
|
|
40
|
-
xr?: boolean | "contained" | "spatialized";
|
|
41
41
|
onClose?: () => void;
|
|
42
|
+
activeIndicatorTransition?: Transition;
|
|
42
43
|
children: React.ReactNode;
|
|
43
44
|
className?: string;
|
|
44
45
|
style?: React.CSSProperties;
|
|
@@ -47,8 +48,8 @@ export interface NavigationRailProps {
|
|
|
47
48
|
const NavigationRailContext = React.createContext<{
|
|
48
49
|
variant: NavigationRailVariant;
|
|
49
50
|
labelVisibility: NavigationRailLabelVisibility;
|
|
50
|
-
|
|
51
|
-
}>({ variant: "collapsed", labelVisibility: "labeled"
|
|
51
|
+
activeIndicatorTransition?: Transition;
|
|
52
|
+
}>({ variant: "collapsed", labelVisibility: "labeled" });
|
|
52
53
|
|
|
53
54
|
const MD3_MODAL_TRANSITION = {
|
|
54
55
|
type: "tween",
|
|
@@ -65,19 +66,16 @@ const railContainerVariants = cva(
|
|
|
65
66
|
{
|
|
66
67
|
variants: {
|
|
67
68
|
variant: {
|
|
68
|
-
collapsed: "items-center",
|
|
69
|
-
expanded: "items-start",
|
|
69
|
+
collapsed: "items-center h-full pt-11 pb-4 shadow-none bg-m3-surface rounded-none",
|
|
70
|
+
expanded: "items-start h-full pt-11 pb-4 shadow-none bg-m3-surface rounded-none",
|
|
70
71
|
modal:
|
|
71
|
-
"bg-m3-surface shadow-lg rounded-r-[var(--m3-shape-corner-large)]",
|
|
72
|
+
"bg-m3-surface shadow-lg rounded-r-[var(--m3-shape-corner-large)] h-full pt-11 pb-4",
|
|
73
|
+
xr: "h-fit py-5 rounded-[48px] shadow-xl bg-m3-surface border border-white/5",
|
|
72
74
|
},
|
|
73
75
|
narrow: {
|
|
74
76
|
true: "w-20",
|
|
75
77
|
false: "w-24",
|
|
76
78
|
},
|
|
77
|
-
xr: {
|
|
78
|
-
true: "h-fit py-5 rounded-[48px] shadow-xl bg-m3-surface border border-white/5",
|
|
79
|
-
false: "h-full pt-11 pb-4 shadow-none bg-m3-surface rounded-none",
|
|
80
|
-
},
|
|
81
79
|
},
|
|
82
80
|
compoundVariants: [
|
|
83
81
|
{ variant: "expanded", className: "min-w-[13.75rem] max-w-[22.5rem]" },
|
|
@@ -86,7 +84,6 @@ const railContainerVariants = cva(
|
|
|
86
84
|
defaultVariants: {
|
|
87
85
|
variant: "collapsed",
|
|
88
86
|
narrow: false,
|
|
89
|
-
xr: false,
|
|
90
87
|
},
|
|
91
88
|
},
|
|
92
89
|
);
|
|
@@ -136,6 +133,8 @@ interface ActivePillProps {
|
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
function ActivePill({ layoutId, disableInitial = false }: ActivePillProps) {
|
|
136
|
+
const { activeIndicatorTransition } = React.useContext(NavigationRailContext);
|
|
137
|
+
|
|
139
138
|
return (
|
|
140
139
|
<m.div
|
|
141
140
|
layoutId={layoutId}
|
|
@@ -144,7 +143,7 @@ function ActivePill({ layoutId, disableInitial = false }: ActivePillProps) {
|
|
|
144
143
|
initial={disableInitial ? false : { opacity: 0, scale: 0.5 }}
|
|
145
144
|
animate={{ opacity: 1, scale: 1 }}
|
|
146
145
|
exit={{ opacity: 0, scale: 0.5, transition: { duration: 0.15 } }}
|
|
147
|
-
transition={SPRING_TRANSITION_EXPRESSIVE}
|
|
146
|
+
transition={activeIndicatorTransition || SPRING_TRANSITION_EXPRESSIVE}
|
|
148
147
|
/>
|
|
149
148
|
);
|
|
150
149
|
}
|
|
@@ -431,11 +430,12 @@ const NavigationRailComponent = React.forwardRef<
|
|
|
431
430
|
labelVisibility = "labeled",
|
|
432
431
|
header,
|
|
433
432
|
fab,
|
|
433
|
+
fabPlacement = "contained",
|
|
434
434
|
footer,
|
|
435
435
|
narrow = false,
|
|
436
436
|
open = false,
|
|
437
|
-
xr = false,
|
|
438
437
|
onClose,
|
|
438
|
+
activeIndicatorTransition,
|
|
439
439
|
children,
|
|
440
440
|
className,
|
|
441
441
|
style,
|
|
@@ -443,9 +443,8 @@ const NavigationRailComponent = React.forwardRef<
|
|
|
443
443
|
ref,
|
|
444
444
|
) => {
|
|
445
445
|
const isModal = variant === "modal";
|
|
446
|
-
const isXr =
|
|
447
|
-
const
|
|
448
|
-
const isSpatial = isXr && xrMode === "spatialized";
|
|
446
|
+
const isXr = variant === "xr";
|
|
447
|
+
const isSpatial = isXr && fabPlacement === "spatialized";
|
|
449
448
|
const applyAnimation = !isXr || !isSpatial;
|
|
450
449
|
|
|
451
450
|
const navRef = React.useRef<HTMLElement>(null);
|
|
@@ -461,13 +460,13 @@ const NavigationRailComponent = React.forwardRef<
|
|
|
461
460
|
);
|
|
462
461
|
|
|
463
462
|
const navBaseClasses = cn(
|
|
464
|
-
railContainerVariants({ variant, narrow
|
|
463
|
+
railContainerVariants({ variant, narrow }),
|
|
465
464
|
);
|
|
466
465
|
const modalPositioning = isModal ? "fixed left-0 top-0 z-[100]" : "";
|
|
467
466
|
|
|
468
467
|
const navHeaderSpacing = (() => {
|
|
469
468
|
if (!isXr) return "mb-6 min-h-10";
|
|
470
|
-
if (
|
|
469
|
+
if (fabPlacement === "contained") return fab ? "mb-10" : "mb-5";
|
|
471
470
|
return "mb-5";
|
|
472
471
|
})();
|
|
473
472
|
|
|
@@ -564,7 +563,7 @@ const NavigationRailComponent = React.forwardRef<
|
|
|
564
563
|
|
|
565
564
|
const finalNavElement = isSpatial ? spatialWrapper : navElement;
|
|
566
565
|
|
|
567
|
-
const contextValue = { variant, labelVisibility,
|
|
566
|
+
const contextValue = { variant, labelVisibility, activeIndicatorTransition };
|
|
568
567
|
|
|
569
568
|
if (isModal) {
|
|
570
569
|
if (typeof document === "undefined") return null;
|
package/src/ui/scroll-area.tsx
CHANGED
|
@@ -32,6 +32,8 @@ export interface ScrollAreaProps
|
|
|
32
32
|
scrollHideDelay?: number;
|
|
33
33
|
/** Extra classes applied to the inner viewport element. */
|
|
34
34
|
viewportClassName?: string;
|
|
35
|
+
/** Ref to the scrolling viewport element. */
|
|
36
|
+
viewportRef?: React.Ref<HTMLDivElement>;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
// ─── Root ─────────────────────────────────────────────────────────────────────
|
|
@@ -47,6 +49,7 @@ const ScrollArea = React.forwardRef<
|
|
|
47
49
|
type = "hover",
|
|
48
50
|
orientation = "vertical",
|
|
49
51
|
scrollHideDelay = 600,
|
|
52
|
+
viewportRef,
|
|
50
53
|
...props
|
|
51
54
|
},
|
|
52
55
|
ref,
|
|
@@ -62,6 +65,7 @@ const ScrollArea = React.forwardRef<
|
|
|
62
65
|
{...props}
|
|
63
66
|
>
|
|
64
67
|
<RadixScrollArea.Viewport
|
|
68
|
+
ref={viewportRef}
|
|
65
69
|
className={cn(
|
|
66
70
|
"h-full w-full flex-1 min-h-0 min-w-0 rounded-[inherit]",
|
|
67
71
|
"outline-none focus-visible:ring-2 focus-visible:ring-m3-primary focus-visible:ring-offset-1",
|
package/test_output.txt
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @bug-on/md3-react@2.0.3 test
|
|
3
|
-
> vitest run src/ui/menu/menu.test.tsx
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
RUN v4.1.0 /Users/stark/Documents/GitHub/bug-on-md3-expressive/packages/react
|
|
7
|
-
|
|
8
|
-
❯ src/ui/menu/menu.test.tsx (28 tests | 8 failed) 16014ms
|
|
9
|
-
✓ renders children when open 277ms
|
|
10
|
-
× MenuItem applies correct shape class for each itemPosition 82ms
|
|
11
|
-
✓ MenuItem shows check icon when selected 83ms
|
|
12
|
-
✓ MenuItem applies disabled state — opacity class and aria-disabled 66ms
|
|
13
|
-
× MenuGroup auto-injects correct itemPosition based on child index 56ms
|
|
14
|
-
× MenuDivider renders with correct role and classes 51ms
|
|
15
|
-
× Standard variant applies surface-container-low on MenuGroup 49ms
|
|
16
|
-
× Vibrant variant applies tertiary-container on MenuGroup 49ms
|
|
17
|
-
✓ Keyboard ArrowDown: menu opens via click 91ms
|
|
18
|
-
✓ Keyboard Escape closes menu 77ms
|
|
19
|
-
✓ renders children directly without a trigger 4ms
|
|
20
|
-
✓ gap separatorStyle renders no divider elements between groups 3ms
|
|
21
|
-
✓ divider separatorStyle inserts an hr between each pair of groups 5ms
|
|
22
|
-
✓ VerticalMenuDivider renders as hr with correct classes 2ms
|
|
23
|
-
✓ auto-injects index and count props into group children for shape morphing 3ms
|
|
24
|
-
✓ standard colorVariant gap: root is transparent, MenuGroup has surface-container-low 3ms
|
|
25
|
-
✓ vibrant colorVariant gap: root is transparent, MenuGroup has tertiary-container 2ms
|
|
26
|
-
✓ standard colorVariant divider: VerticalMenuContent has surface-container-low 2ms
|
|
27
|
-
✓ VerticalMenu root has role=menu and aria-orientation=vertical 2ms
|
|
28
|
-
✓ ArrowDown key moves focus to next menuitem 14ms
|
|
29
|
-
✓ ArrowUp from first item wraps to last item 9ms
|
|
30
|
-
✓ MenuItem inside VerticalMenu shows check icon when selected=true 4ms
|
|
31
|
-
✓ renders trigger item correctly 35ms
|
|
32
|
-
× opens after hoverOpenDelay (default 200ms) 5009ms
|
|
33
|
-
× closes after hoverCloseDelay (default 300ms) 5008ms
|
|
34
|
-
✓ useMenuContext returns default values when used outside Provider 4ms
|
|
35
|
-
× MenuGroup updates state on hover for shape morphing 5003ms
|
|
36
|
-
✓ FAST_SPATIAL_SPRING has correct spring parameters 0ms
|
|
37
|
-
|
|
38
|
-
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 8 ⎯⎯⎯⎯⎯⎯⎯
|
|
39
|
-
|
|
40
|
-
FAIL src/ui/menu/menu.test.tsx > Menu > MenuItem applies correct shape class for each itemPosition
|
|
41
|
-
AssertionError: expected 'relative flex w-full cursor-pointer s…' to contain 'rounded-t-[12px]'
|
|
42
|
-
|
|
43
|
-
Expected: "rounded-t-[12px]"
|
|
44
|
-
Received: "relative flex w-full cursor-pointer select-none items-center outline-none min-h-12 min-w-28 max-w-70 px-3 rounded-none transition-[border-radius,background-color] duration-150 ease-in text-m3-on-surface hover:bg-m3-on-surface/8 focus:bg-m3-on-surface/12 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-m3-primary"
|
|
45
|
-
|
|
46
|
-
❯ src/ui/menu/menu.test.tsx:90:29
|
|
47
|
-
88|
|
|
48
|
-
89| // Shape classes based on ITEM_SHAPE_CLASSES token
|
|
49
|
-
90| expect(leading.className).toContain("rounded-t-[12px]");
|
|
50
|
-
| ^
|
|
51
|
-
91| expect(leading.className).toContain("rounded-b-[4px]");
|
|
52
|
-
92| expect(middle.className).toContain("rounded-[4px]");
|
|
53
|
-
|
|
54
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/8]⎯
|
|
55
|
-
|
|
56
|
-
FAIL src/ui/menu/menu.test.tsx > Menu > MenuGroup auto-injects correct itemPosition based on child index
|
|
57
|
-
AssertionError: expected 'relative flex w-full cursor-pointer s…' to contain 'rounded-t-[12px]'
|
|
58
|
-
|
|
59
|
-
Expected: "rounded-t-[12px]"
|
|
60
|
-
Received: "relative flex w-full cursor-pointer select-none items-center outline-none min-h-12 min-w-28 max-w-70 px-3 rounded-none transition-[border-radius,background-color] duration-150 ease-in text-m3-on-surface hover:bg-m3-on-surface/8 focus:bg-m3-on-surface/12 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-m3-primary"
|
|
61
|
-
|
|
62
|
-
❯ src/ui/menu/menu.test.tsx:146:27
|
|
63
|
-
144|
|
|
64
|
-
145| // First item should be "leading" shape: rounded-t-[12px] rounded-b-…
|
|
65
|
-
146| expect(first.className).toContain("rounded-t-[12px]");
|
|
66
|
-
| ^
|
|
67
|
-
147| // Last item should be "trailing" shape: rounded-t-[4px] rounded-b-[…
|
|
68
|
-
148| expect(last.className).toContain("rounded-b-[12px]");
|
|
69
|
-
|
|
70
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/8]⎯
|
|
71
|
-
|
|
72
|
-
FAIL src/ui/menu/menu.test.tsx > Menu > MenuDivider renders with correct role and classes
|
|
73
|
-
AssertionError: expected 'my-2 mx-0 h-px border-0 bg-m3-outline…' to contain 'mx-3'
|
|
74
|
-
|
|
75
|
-
Expected: "mx-3"
|
|
76
|
-
Received: "my-2 mx-0 h-px border-0 bg-m3-outline-variant"
|
|
77
|
-
|
|
78
|
-
❯ src/ui/menu/menu.test.tsx:167:29
|
|
79
|
-
165| const divider = screen.getByTestId("divider");
|
|
80
|
-
166| expect(divider.getAttribute("role")).toBe("separator");
|
|
81
|
-
167| expect(divider.className).toContain("mx-3");
|
|
82
|
-
| ^
|
|
83
|
-
168| expect(divider.className).toContain("bg-m3-outline-variant");
|
|
84
|
-
169| });
|
|
85
|
-
|
|
86
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/8]⎯
|
|
87
|
-
|
|
88
|
-
FAIL src/ui/menu/menu.test.tsx > Menu > Standard variant applies surface-container-low on MenuGroup
|
|
89
|
-
AssertionError: expected 'relative overflow-hidden py-1 bg-tran…' to contain 'bg-m3-surface-container-low'
|
|
90
|
-
|
|
91
|
-
Expected: "bg-m3-surface-container-low"
|
|
92
|
-
Received: "relative overflow-hidden py-1 bg-transparent"
|
|
93
|
-
|
|
94
|
-
❯ src/ui/menu/menu.test.tsx:186:27
|
|
95
|
-
184| );
|
|
96
|
-
185| const group = screen.getByTestId("group-standard");
|
|
97
|
-
186| expect(group.className).toContain("bg-m3-surface-container-low");
|
|
98
|
-
| ^
|
|
99
|
-
187| });
|
|
100
|
-
188|
|
|
101
|
-
|
|
102
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/8]⎯
|
|
103
|
-
|
|
104
|
-
FAIL src/ui/menu/menu.test.tsx > Menu > Vibrant variant applies tertiary-container on MenuGroup
|
|
105
|
-
AssertionError: expected 'relative overflow-hidden py-1 bg-tran…' to contain 'bg-m3-tertiary-container'
|
|
106
|
-
|
|
107
|
-
Expected: "bg-m3-tertiary-container"
|
|
108
|
-
Received: "relative overflow-hidden py-1 bg-transparent"
|
|
109
|
-
|
|
110
|
-
❯ src/ui/menu/menu.test.tsx:204:27
|
|
111
|
-
202| );
|
|
112
|
-
203| const group = screen.getByTestId("group-vibrant");
|
|
113
|
-
204| expect(group.className).toContain("bg-m3-tertiary-container");
|
|
114
|
-
| ^
|
|
115
|
-
205| });
|
|
116
|
-
206|
|
|
117
|
-
|
|
118
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/8]⎯
|
|
119
|
-
|
|
120
|
-
FAIL src/ui/menu/menu.test.tsx > SubMenu > opens after hoverOpenDelay (default 200ms)
|
|
121
|
-
Error: Test timed out in 5000ms.
|
|
122
|
-
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
|
|
123
|
-
❯ src/ui/menu/menu.test.tsx:513:2
|
|
124
|
-
511|
|
|
125
|
-
512| // 2. SubMenu opens after hover delay
|
|
126
|
-
513| it("opens after hoverOpenDelay (default 200ms)", async () => {
|
|
127
|
-
| ^
|
|
128
|
-
514| vi.useFakeTimers();
|
|
129
|
-
515| const user = userEvent.setup({ delay: null });
|
|
130
|
-
|
|
131
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/8]⎯
|
|
132
|
-
|
|
133
|
-
FAIL src/ui/menu/menu.test.tsx > SubMenu > closes after hoverCloseDelay (default 300ms)
|
|
134
|
-
Error: Test timed out in 5000ms.
|
|
135
|
-
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
|
|
136
|
-
❯ src/ui/menu/menu.test.tsx:553:2
|
|
137
|
-
551|
|
|
138
|
-
552| // 3. SubMenu closes after hover close delay
|
|
139
|
-
553| it("closes after hoverCloseDelay (default 300ms)", async () => {
|
|
140
|
-
| ^
|
|
141
|
-
554| vi.useFakeTimers();
|
|
142
|
-
555| const user = userEvent.setup({ delay: null });
|
|
143
|
-
|
|
144
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/8]⎯
|
|
145
|
-
|
|
146
|
-
FAIL src/ui/menu/menu.test.tsx > Menu Internals > MenuGroup updates state on hover for shape morphing
|
|
147
|
-
Error: Test timed out in 5000ms.
|
|
148
|
-
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
|
|
149
|
-
❯ src/ui/menu/menu.test.tsx:616:2
|
|
150
|
-
614|
|
|
151
|
-
615| // 2. MenuGroup shape morphing triggers on hover
|
|
152
|
-
616| it("MenuGroup updates state on hover for shape morphing", async () =>…
|
|
153
|
-
| ^
|
|
154
|
-
617| const user = userEvent.setup();
|
|
155
|
-
618| render(
|
|
156
|
-
|
|
157
|
-
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/8]⎯
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Test Files 1 failed (1)
|
|
161
|
-
Tests 8 failed | 20 passed (28)
|
|
162
|
-
Start at 09:33:33
|
|
163
|
-
Duration 17.69s (transform 152ms, setup 97ms, import 567ms, tests 16.01s, environment 805ms)
|
|
164
|
-
|