@bug-on/md3-react 2.0.2 → 3.0.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/.turbo/turbo-build.log +33 -0
- package/CHANGELOG.md +55 -0
- package/dist/index.css +23 -0
- package/dist/index.css.d.ts +2 -0
- package/dist/index.d.mts +6127 -0
- package/dist/index.d.ts +6127 -69
- package/dist/index.js +2536 -665
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2443 -603
- package/dist/index.mjs.map +1 -1
- package/dist/material-symbols-cdn.css.d.ts +2 -0
- package/dist/material-symbols-self-hosted.css.d.ts +2 -0
- package/dist/typography.css.d.ts +2 -0
- package/package.json +23 -19
- package/scripts/copy-assets.js +82 -0
- package/src/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
- package/src/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
- package/src/assets/loading-indicator.svg +19 -0
- package/src/assets/material-symbols-cdn.css +65 -0
- package/src/assets/material-symbols-self-hosted.css +90 -0
- package/src/css.d.ts +20 -0
- package/{dist/hooks/index.d.ts → src/hooks/index.ts} +1 -0
- package/src/hooks/useClickOutside.ts +37 -0
- package/src/hooks/useMediaQuery.ts +28 -0
- package/src/hooks/useRipple.ts +88 -0
- package/src/index.css +23 -0
- package/src/index.ts +349 -0
- package/src/lib/material-symbols-preconnect.tsx +82 -0
- package/src/lib/theme-utils.ts +180 -0
- package/src/lib/utils.ts +6 -0
- package/src/test/button.test.tsx +59 -0
- package/src/test/icon.test.tsx +91 -0
- package/src/test/loading-indicator.test.tsx +128 -0
- package/src/test/progress-indicator.test.tsx +306 -0
- package/src/test/setup.ts +80 -0
- package/src/test/typography.test.tsx +206 -0
- package/src/types/index.ts +7 -0
- package/src/types/md3.ts +31 -0
- package/src/ui/Text.tsx +60 -0
- package/src/ui/__snapshots__/divider.test.tsx.snap +63 -0
- package/src/ui/app-bar/app-bar-column.tsx +99 -0
- package/src/ui/app-bar/app-bar-item-button.tsx +71 -0
- package/src/ui/app-bar/app-bar-items.test.tsx +89 -0
- package/src/ui/app-bar/app-bar-overflow-indicator.tsx +108 -0
- package/src/ui/app-bar/app-bar-row.tsx +104 -0
- package/src/ui/app-bar/app-bar.test.tsx +87 -0
- package/src/ui/app-bar/app-bar.tokens.ts +223 -0
- package/src/ui/app-bar/app-bar.types.ts +441 -0
- package/src/ui/app-bar/bottom-app-bar.test.tsx +42 -0
- package/src/ui/app-bar/bottom-app-bar.tsx +84 -0
- package/src/ui/app-bar/docked-toolbar.test.tsx +34 -0
- package/src/ui/app-bar/docked-toolbar.tsx +54 -0
- package/src/ui/app-bar/flexible-app-bar.test.tsx +75 -0
- package/src/ui/app-bar/hooks/use-app-bar-scroll.ts +110 -0
- package/src/ui/app-bar/hooks/use-flexible-app-bar.ts +123 -0
- package/{dist/ui/app-bar/index.d.ts → src/ui/app-bar/index.ts} +35 -2
- package/src/ui/app-bar/large-flexible-app-bar.tsx +165 -0
- package/src/ui/app-bar/medium-flexible-app-bar.tsx +167 -0
- package/src/ui/app-bar/search-app-bar.test.tsx +49 -0
- package/src/ui/app-bar/search-app-bar.tsx +176 -0
- package/src/ui/app-bar/search-view.tsx +227 -0
- package/src/ui/app-bar/small-app-bar.test.tsx +48 -0
- package/src/ui/app-bar/small-app-bar.tsx +203 -0
- package/src/ui/badge.test.tsx +345 -0
- package/src/ui/badge.tsx +282 -0
- package/src/ui/button-group.test.tsx +71 -0
- package/src/ui/button-group.tsx +350 -0
- package/src/ui/button.test.tsx +297 -0
- package/src/ui/button.tsx +669 -0
- package/src/ui/card.test.tsx +187 -0
- package/src/ui/card.tsx +259 -0
- package/src/ui/checkbox.test.tsx +423 -0
- package/src/ui/checkbox.tsx +525 -0
- package/src/ui/chip.test.tsx +292 -0
- package/src/ui/chip.tsx +548 -0
- package/src/ui/code-block.tsx +219 -0
- package/src/ui/dialog.test.tsx +300 -0
- package/src/ui/dialog.tsx +384 -0
- package/src/ui/divider.test.tsx +314 -0
- package/src/ui/divider.tsx +412 -0
- package/src/ui/drawer.tsx +240 -0
- package/src/ui/fab-menu.test.tsx +494 -0
- package/src/ui/fab-menu.tsx +739 -0
- package/src/ui/fab.test.tsx +232 -0
- package/src/ui/fab.tsx +505 -0
- package/src/ui/icon-button.test.tsx +515 -0
- package/src/ui/icon-button.tsx +525 -0
- package/src/ui/icon.test.tsx +197 -0
- package/src/ui/icon.tsx +179 -0
- package/src/ui/loading-indicator.test.tsx +73 -0
- package/src/ui/loading-indicator.tsx +312 -0
- package/src/ui/menu/context-menu.tsx +275 -0
- package/src/ui/menu/index.ts +77 -0
- package/src/ui/menu/menu-animations.ts +102 -0
- package/src/ui/menu/menu-context.tsx +99 -0
- package/src/ui/menu/menu-divider.tsx +47 -0
- package/src/ui/menu/menu-group.tsx +200 -0
- package/src/ui/menu/menu-item.tsx +294 -0
- package/src/ui/menu/menu-tokens.ts +208 -0
- package/src/ui/menu/menu-types.ts +313 -0
- package/src/ui/menu/menu.test.tsx +624 -0
- package/src/ui/menu/menu.tsx +289 -0
- package/src/ui/menu/sub-menu.tsx +223 -0
- package/src/ui/menu/vertical-menu.tsx +382 -0
- package/src/ui/navigation-rail.test.tsx +404 -0
- package/src/ui/navigation-rail.tsx +604 -0
- package/src/ui/progress-indicator/circular.tsx +248 -0
- package/src/ui/progress-indicator/hooks.ts +51 -0
- package/{dist/ui/progress-indicator/index.d.ts → src/ui/progress-indicator/index.tsx} +20 -2
- package/src/ui/progress-indicator/linear-flat.tsx +83 -0
- package/src/ui/progress-indicator/linear-wavy.tsx +243 -0
- package/src/ui/progress-indicator/linear.tsx +143 -0
- package/src/ui/progress-indicator/types.ts +158 -0
- package/src/ui/progress-indicator/utils.ts +73 -0
- package/src/ui/radio-button.test.tsx +407 -0
- package/src/ui/radio-button.tsx +551 -0
- package/src/ui/ripple.test.tsx +72 -0
- package/src/ui/ripple.tsx +234 -0
- package/src/ui/scroll-area.test.tsx +58 -0
- package/src/ui/scroll-area.tsx +139 -0
- package/src/ui/search/animated-placeholder.tsx +145 -0
- package/src/ui/search/hooks/use-search-keyboard.test.ts +202 -0
- package/src/ui/search/hooks/use-search-keyboard.ts +104 -0
- package/src/ui/search/hooks/use-search-view-focus.test.ts +96 -0
- package/src/ui/search/hooks/use-search-view-focus.ts +24 -0
- package/src/ui/search/index.ts +44 -0
- package/src/ui/search/search-bar.tsx +220 -0
- package/src/ui/search/search-context.tsx +42 -0
- package/src/ui/search/search-view-docked.tsx +194 -0
- package/src/ui/search/search-view-fullscreen.tsx +247 -0
- package/src/ui/search/search.test.tsx +233 -0
- package/src/ui/search/search.tokens.ts +134 -0
- package/src/ui/search/search.tsx +131 -0
- package/src/ui/search/search.types.ts +154 -0
- package/src/ui/search/trailing-action.tsx +49 -0
- package/src/ui/shared/constants.ts +122 -0
- package/{dist/ui/shared/touch-target.d.ts → src/ui/shared/touch-target.tsx} +13 -1
- package/src/ui/slider/hooks/useSliderMath.ts +195 -0
- package/{dist/ui/slider/index.d.ts → src/ui/slider/index.ts} +12 -1
- package/src/ui/slider/range-slider.tsx +561 -0
- package/src/ui/slider/slider-thumb.tsx +379 -0
- package/src/ui/slider/slider-track.tsx +912 -0
- package/src/ui/slider/slider.tokens.ts +189 -0
- package/src/ui/slider/slider.tsx +259 -0
- package/src/ui/slider/slider.types.ts +288 -0
- package/src/ui/snackbar/index.ts +20 -0
- package/src/ui/snackbar/snackbar.test.tsx +338 -0
- package/src/ui/snackbar/snackbar.tsx +476 -0
- package/{dist/ui/switch/index.d.ts → src/ui/switch/index.ts} +1 -0
- package/src/ui/switch/switch.stories.tsx +309 -0
- package/src/ui/switch/switch.test.tsx +243 -0
- package/src/ui/switch/switch.tokens.ts +89 -0
- package/src/ui/switch/switch.tsx +504 -0
- package/src/ui/switch/switch.types.ts +62 -0
- package/{dist/ui/tabs/index.d.ts → src/ui/tabs/index.ts} +8 -1
- package/src/ui/tabs/tab.tsx +407 -0
- package/src/ui/tabs/tabs-content.tsx +89 -0
- package/src/ui/tabs/tabs-list.tsx +146 -0
- package/src/ui/tabs/tabs.test.tsx +290 -0
- package/src/ui/tabs/tabs.tokens.ts +121 -0
- package/src/ui/tabs/tabs.tsx +229 -0
- package/src/ui/tabs/tabs.types.ts +185 -0
- package/{dist/ui/text-field/index.d.ts → src/ui/text-field/index.ts} +8 -1
- package/src/ui/text-field/subcomponents/active-indicator.tsx +67 -0
- package/src/ui/text-field/subcomponents/floating-label.tsx +161 -0
- package/src/ui/text-field/subcomponents/leading-icon.tsx +46 -0
- package/src/ui/text-field/subcomponents/outline-container.tsx +170 -0
- package/src/ui/text-field/subcomponents/prefix-suffix.tsx +59 -0
- package/src/ui/text-field/subcomponents/supporting-text.tsx +145 -0
- package/src/ui/text-field/subcomponents/trailing-icon.tsx +199 -0
- package/src/ui/text-field/text-field.test.tsx +454 -0
- package/src/ui/text-field/text-field.tokens.ts +104 -0
- package/src/ui/text-field/text-field.tsx +548 -0
- package/src/ui/text-field/text-field.types.ts +180 -0
- package/src/ui/theme-provider/index.tsx +190 -0
- package/src/ui/toc.test.tsx +108 -0
- package/src/ui/toc.tsx +172 -0
- package/src/ui/tooltip/plain-tooltip.tsx +63 -0
- package/src/ui/tooltip/rich-tooltip.tsx +94 -0
- package/src/ui/tooltip/tooltip-box.tsx +266 -0
- package/src/ui/tooltip/tooltip-caret-shape.tsx +68 -0
- package/src/ui/tooltip/tooltip.tokens.ts +26 -0
- package/src/ui/tooltip/tooltip.types.ts +70 -0
- package/src/ui/tooltip/use-tooltip-position.ts +208 -0
- package/src/ui/tooltip/use-tooltip-state.ts +41 -0
- package/src/ui/typography/__tests__/typography.test.tsx +170 -0
- package/{dist/ui/typography/index.d.ts → src/ui/typography/index.ts} +21 -3
- package/src/ui/typography/type-scale-tokens.ts +205 -0
- package/src/ui/typography/typography-key-tokens.ts +43 -0
- package/src/ui/typography/typography-tokens.ts +360 -0
- package/src/ui/typography/typography.css +22 -0
- package/src/ui/typography/typography.tsx +559 -0
- package/test-render.tsx +4 -0
- package/test-shadow.html +26 -0
- package/test_output.txt +164 -0
- package/test_output_v2.txt +5 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +18 -0
- package/tsup.config.ts +20 -0
- package/vitest.config.ts +11 -0
- package/dist/hooks/useMediaQuery.d.ts +0 -11
- package/dist/hooks/useRipple.d.ts +0 -26
- package/dist/lib/material-symbols-preconnect.d.ts +0 -42
- package/dist/lib/theme-utils.d.ts +0 -63
- package/dist/lib/utils.d.ts +0 -2
- package/dist/types/index.d.ts +0 -1
- package/dist/types/md3.d.ts +0 -14
- package/dist/ui/app-bar/app-bar-column.d.ts +0 -28
- package/dist/ui/app-bar/app-bar-item-button.d.ts +0 -16
- package/dist/ui/app-bar/app-bar-overflow-indicator.d.ts +0 -18
- package/dist/ui/app-bar/app-bar-row.d.ts +0 -36
- package/dist/ui/app-bar/app-bar.tokens.d.ts +0 -184
- package/dist/ui/app-bar/app-bar.types.d.ts +0 -392
- package/dist/ui/app-bar/bottom-app-bar.d.ts +0 -31
- package/dist/ui/app-bar/docked-toolbar.d.ts +0 -25
- package/dist/ui/app-bar/hooks/use-app-bar-scroll.d.ts +0 -42
- package/dist/ui/app-bar/hooks/use-flexible-app-bar.d.ts +0 -37
- package/dist/ui/app-bar/large-flexible-app-bar.d.ts +0 -26
- package/dist/ui/app-bar/medium-flexible-app-bar.d.ts +0 -28
- package/dist/ui/app-bar/search-app-bar.d.ts +0 -43
- package/dist/ui/app-bar/search-view.d.ts +0 -54
- package/dist/ui/app-bar/small-app-bar.d.ts +0 -37
- package/dist/ui/badge.d.ts +0 -125
- package/dist/ui/button-group.d.ts +0 -59
- package/dist/ui/button.d.ts +0 -148
- package/dist/ui/card.d.ts +0 -62
- package/dist/ui/checkbox.d.ts +0 -82
- package/dist/ui/chip.d.ts +0 -110
- package/dist/ui/code-block.d.ts +0 -14
- package/dist/ui/dialog.d.ts +0 -111
- package/dist/ui/divider.d.ts +0 -164
- package/dist/ui/drawer.d.ts +0 -39
- package/dist/ui/dropdown.d.ts +0 -29
- package/dist/ui/fab-menu.d.ts +0 -204
- package/dist/ui/fab.d.ts +0 -162
- package/dist/ui/icon-button.d.ts +0 -131
- package/dist/ui/icon.d.ts +0 -88
- package/dist/ui/loading-indicator.d.ts +0 -42
- package/dist/ui/navigation-rail.d.ts +0 -29
- package/dist/ui/progress-indicator/circular.d.ts +0 -3
- package/dist/ui/progress-indicator/hooks.d.ts +0 -3
- package/dist/ui/progress-indicator/linear-flat.d.ts +0 -10
- package/dist/ui/progress-indicator/linear-wavy.d.ts +0 -18
- package/dist/ui/progress-indicator/linear.d.ts +0 -3
- package/dist/ui/progress-indicator/types.d.ts +0 -151
- package/dist/ui/progress-indicator/utils.d.ts +0 -3
- package/dist/ui/radio-button.d.ts +0 -106
- package/dist/ui/ripple.d.ts +0 -126
- package/dist/ui/scroll-area.d.ts +0 -27
- package/dist/ui/shared/constants.d.ts +0 -86
- package/dist/ui/slider/hooks/useSliderMath.d.ts +0 -101
- package/dist/ui/slider/range-slider.d.ts +0 -47
- package/dist/ui/slider/slider-thumb.d.ts +0 -33
- package/dist/ui/slider/slider-track.d.ts +0 -25
- package/dist/ui/slider/slider.d.ts +0 -60
- package/dist/ui/slider/slider.tokens.d.ts +0 -151
- package/dist/ui/slider/slider.types.d.ts +0 -259
- package/dist/ui/snackbar/index.d.ts +0 -6
- package/dist/ui/snackbar/snackbar.d.ts +0 -197
- package/dist/ui/switch/switch.d.ts +0 -30
- package/dist/ui/switch/switch.stories.d.ts +0 -48
- package/dist/ui/switch/switch.tokens.d.ts +0 -67
- package/dist/ui/switch/switch.types.d.ts +0 -59
- package/dist/ui/tabs/tab.d.ts +0 -43
- package/dist/ui/tabs/tabs-content.d.ts +0 -36
- package/dist/ui/tabs/tabs-list.d.ts +0 -40
- package/dist/ui/tabs/tabs.d.ts +0 -60
- package/dist/ui/tabs/tabs.tokens.d.ts +0 -94
- package/dist/ui/tabs/tabs.types.d.ts +0 -172
- package/dist/ui/text-field/subcomponents/active-indicator.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/floating-label.d.ts +0 -43
- package/dist/ui/text-field/subcomponents/leading-icon.d.ts +0 -23
- package/dist/ui/text-field/subcomponents/outline-container.d.ts +0 -42
- package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +0 -24
- package/dist/ui/text-field/subcomponents/supporting-text.d.ts +0 -37
- package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +0 -41
- package/dist/ui/text-field/text-field.d.ts +0 -49
- package/dist/ui/text-field/text-field.tokens.d.ts +0 -76
- package/dist/ui/text-field/text-field.types.d.ts +0 -126
- package/dist/ui/theme-provider/index.d.ts +0 -48
- package/dist/ui/toc.d.ts +0 -80
- package/dist/ui/tooltip/plain-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/rich-tooltip.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-box.d.ts +0 -2
- package/dist/ui/tooltip/tooltip-caret-shape.d.ts +0 -9
- package/dist/ui/tooltip/tooltip.tokens.d.ts +0 -26
- package/dist/ui/tooltip/tooltip.types.d.ts +0 -56
- package/dist/ui/tooltip/use-tooltip-position.d.ts +0 -8
- package/dist/ui/tooltip/use-tooltip-state.d.ts +0 -2
- package/dist/ui/typography/type-scale-tokens.d.ts +0 -162
- package/dist/ui/typography/typography-key-tokens.d.ts +0 -40
- package/dist/ui/typography/typography-tokens.d.ts +0 -220
- package/dist/ui/typography/typography.d.ts +0 -265
- /package/{dist/ui/tooltip/index.d.ts → src/ui/tooltip/index.ts} +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file navigation-rail.test.tsx
|
|
3
|
+
*
|
|
4
|
+
* Test suite for the NavigationRail component.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
8
|
+
import { describe, expect, it, vi } from "vitest";
|
|
9
|
+
|
|
10
|
+
import { NavigationRail, NavigationRailItem } from "./navigation-rail";
|
|
11
|
+
|
|
12
|
+
describe("NavigationRail & NavigationRailItem", () => {
|
|
13
|
+
it("renders the navigation rail with collapsed variant by default", () => {
|
|
14
|
+
render(
|
|
15
|
+
<NavigationRail>
|
|
16
|
+
<NavigationRailItem
|
|
17
|
+
selected
|
|
18
|
+
icon={<svg data-testid="icon1" />}
|
|
19
|
+
label="Home"
|
|
20
|
+
/>
|
|
21
|
+
</NavigationRail>,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const nav = screen.getByRole("navigation");
|
|
25
|
+
expect(nav).toBeInTheDocument();
|
|
26
|
+
expect(nav).toHaveClass("w-24"); // default not narrow
|
|
27
|
+
expect(nav).toHaveClass("items-center"); // collapsed layout class
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("renders the navigation rail with expanded variant", () => {
|
|
31
|
+
render(
|
|
32
|
+
<NavigationRail variant="expanded">
|
|
33
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
34
|
+
</NavigationRail>,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const nav = screen.getByRole("navigation");
|
|
38
|
+
expect(nav).toHaveClass("min-w-[13.75rem]");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("renders the modal variant only when open is true", () => {
|
|
42
|
+
const { rerender } = render(
|
|
43
|
+
<NavigationRail variant="modal" open={false}>
|
|
44
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
45
|
+
</NavigationRail>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(screen.queryByRole("navigation")).not.toBeInTheDocument();
|
|
49
|
+
|
|
50
|
+
rerender(
|
|
51
|
+
<NavigationRail variant="modal" open={true}>
|
|
52
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
53
|
+
</NavigationRail>,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const nav = screen.getByRole("navigation");
|
|
57
|
+
expect(nav).toBeInTheDocument();
|
|
58
|
+
expect(nav).toHaveClass("fixed");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("calls onClose when modal backdrop is clicked", () => {
|
|
62
|
+
const handleClose = vi.fn();
|
|
63
|
+
render(
|
|
64
|
+
<NavigationRail variant="modal" open={true} onClose={handleClose}>
|
|
65
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
66
|
+
</NavigationRail>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Backdrop is rendered just before the nav
|
|
70
|
+
const nav = screen.getByRole("navigation");
|
|
71
|
+
const backdrop = nav.previousSibling as HTMLElement;
|
|
72
|
+
expect(backdrop).toHaveClass("z-40");
|
|
73
|
+
|
|
74
|
+
fireEvent.click(backdrop);
|
|
75
|
+
expect(handleClose).toHaveBeenCalledTimes(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("sets correct attributes for items", () => {
|
|
79
|
+
render(
|
|
80
|
+
<NavigationRail>
|
|
81
|
+
<NavigationRailItem
|
|
82
|
+
selected
|
|
83
|
+
icon={<svg />}
|
|
84
|
+
label="Home"
|
|
85
|
+
aria-label="Home custom"
|
|
86
|
+
/>
|
|
87
|
+
<NavigationRailItem selected={false} icon={<svg />} label="Settings" />
|
|
88
|
+
<NavigationRailItem
|
|
89
|
+
selected={false}
|
|
90
|
+
disabled
|
|
91
|
+
icon={<svg />}
|
|
92
|
+
label="Admin"
|
|
93
|
+
/>
|
|
94
|
+
</NavigationRail>,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const items = screen.getAllByRole("menuitem");
|
|
98
|
+
expect(items).toHaveLength(3);
|
|
99
|
+
|
|
100
|
+
const home = items[0];
|
|
101
|
+
expect(home).toHaveAttribute("aria-current", "page");
|
|
102
|
+
expect(home).toHaveAttribute("aria-label", "Home custom");
|
|
103
|
+
|
|
104
|
+
const settings = items[1];
|
|
105
|
+
expect(settings).not.toHaveAttribute("aria-current");
|
|
106
|
+
expect(settings).toHaveAttribute("aria-label", "Settings");
|
|
107
|
+
|
|
108
|
+
const admin = items[2];
|
|
109
|
+
expect(admin).toHaveAttribute("aria-disabled", "true");
|
|
110
|
+
expect(admin).toHaveClass("opacity-[0.38]");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("calls onClick when an item is clicked", () => {
|
|
114
|
+
const handleClick = vi.fn();
|
|
115
|
+
render(
|
|
116
|
+
<NavigationRail>
|
|
117
|
+
<NavigationRailItem
|
|
118
|
+
selected={false}
|
|
119
|
+
icon={<svg />}
|
|
120
|
+
label="Home"
|
|
121
|
+
onClick={handleClick}
|
|
122
|
+
/>
|
|
123
|
+
</NavigationRail>,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const item = screen.getByRole("menuitem");
|
|
127
|
+
fireEvent.click(item);
|
|
128
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("prevents click when an item is disabled", () => {
|
|
132
|
+
const handleClick = vi.fn();
|
|
133
|
+
render(
|
|
134
|
+
<NavigationRail>
|
|
135
|
+
<NavigationRailItem
|
|
136
|
+
selected={false}
|
|
137
|
+
disabled
|
|
138
|
+
icon={<svg />}
|
|
139
|
+
label="Home"
|
|
140
|
+
onClick={handleClick}
|
|
141
|
+
/>
|
|
142
|
+
</NavigationRail>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const item = screen.getByRole("menuitem");
|
|
146
|
+
fireEvent.click(item);
|
|
147
|
+
expect(handleClick).not.toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("initializes roving tabindex with first item", () => {
|
|
151
|
+
render(
|
|
152
|
+
<NavigationRail>
|
|
153
|
+
<NavigationRailItem selected={false} icon={<svg />} label="1" />
|
|
154
|
+
<NavigationRailItem selected={false} icon={<svg />} label="2" />
|
|
155
|
+
</NavigationRail>,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const items = screen.getAllByRole("menuitem");
|
|
159
|
+
expect(items[0]).toHaveAttribute("tabindex", "0");
|
|
160
|
+
expect(items[1]).toHaveAttribute("tabindex", "-1");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("initializes roving tabindex with selected item", () => {
|
|
164
|
+
render(
|
|
165
|
+
<NavigationRail>
|
|
166
|
+
<NavigationRailItem selected={false} icon={<svg />} label="1" />
|
|
167
|
+
<NavigationRailItem selected icon={<svg />} label="2" />
|
|
168
|
+
</NavigationRail>,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const items = screen.getAllByRole("menuitem");
|
|
172
|
+
expect(items[0]).toHaveAttribute("tabindex", "-1");
|
|
173
|
+
expect(items[1]).toHaveAttribute("tabindex", "0");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("does not include disabled items in roving tabindex on init", () => {
|
|
177
|
+
render(
|
|
178
|
+
<NavigationRail>
|
|
179
|
+
<NavigationRailItem
|
|
180
|
+
selected={false}
|
|
181
|
+
disabled
|
|
182
|
+
icon={<svg />}
|
|
183
|
+
label="1"
|
|
184
|
+
/>
|
|
185
|
+
<NavigationRailItem selected={false} icon={<svg />} label="2" />
|
|
186
|
+
</NavigationRail>,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const items = screen.getAllByRole("menuitem");
|
|
190
|
+
expect(items[0]).toHaveAttribute("tabindex", "-1"); // disabled item retains its default -1 tabindex
|
|
191
|
+
expect(items[1]).toHaveAttribute("tabindex", "0");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("moves focus down using ArrowDown", () => {
|
|
195
|
+
render(
|
|
196
|
+
<NavigationRail>
|
|
197
|
+
<NavigationRailItem selected icon={<svg />} label="1" />
|
|
198
|
+
<NavigationRailItem selected={false} icon={<svg />} label="2" />
|
|
199
|
+
</NavigationRail>,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const nav = screen.getByRole("navigation");
|
|
203
|
+
const items = screen.getAllByRole("menuitem");
|
|
204
|
+
|
|
205
|
+
items[0].focus();
|
|
206
|
+
expect(document.activeElement).toBe(items[0]);
|
|
207
|
+
|
|
208
|
+
fireEvent.keyDown(nav, { key: "ArrowDown" });
|
|
209
|
+
expect(document.activeElement).toBe(items[1]);
|
|
210
|
+
expect(items[1]).toHaveAttribute("tabindex", "0");
|
|
211
|
+
expect(items[0]).toHaveAttribute("tabindex", "-1");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("wraps around focus using ArrowDown", () => {
|
|
215
|
+
render(
|
|
216
|
+
<NavigationRail>
|
|
217
|
+
<NavigationRailItem selected={false} icon={<svg />} label="1" />
|
|
218
|
+
<NavigationRailItem selected icon={<svg />} label="2" />
|
|
219
|
+
</NavigationRail>,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const nav = screen.getByRole("navigation");
|
|
223
|
+
const items = screen.getAllByRole("menuitem");
|
|
224
|
+
|
|
225
|
+
items[1].focus();
|
|
226
|
+
fireEvent.keyDown(nav, { key: "ArrowDown" });
|
|
227
|
+
expect(document.activeElement).toBe(items[0]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("moves focus using Home and End keys", () => {
|
|
231
|
+
render(
|
|
232
|
+
<NavigationRail>
|
|
233
|
+
<NavigationRailItem selected={false} icon={<svg />} label="1" />
|
|
234
|
+
<NavigationRailItem selected icon={<svg />} label="2" />
|
|
235
|
+
<NavigationRailItem selected={false} icon={<svg />} label="3" />
|
|
236
|
+
</NavigationRail>,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const nav = screen.getByRole("navigation");
|
|
240
|
+
const items = screen.getAllByRole("menuitem");
|
|
241
|
+
|
|
242
|
+
items[1].focus();
|
|
243
|
+
fireEvent.keyDown(nav, { key: "Home" });
|
|
244
|
+
expect(document.activeElement).toBe(items[0]);
|
|
245
|
+
|
|
246
|
+
fireEvent.keyDown(nav, { key: "End" });
|
|
247
|
+
expect(document.activeElement).toBe(items[2]);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("triggers click on Enter and Space", () => {
|
|
251
|
+
const handleClick = vi.fn();
|
|
252
|
+
render(
|
|
253
|
+
<NavigationRail>
|
|
254
|
+
<NavigationRailItem
|
|
255
|
+
selected={false}
|
|
256
|
+
icon={<svg />}
|
|
257
|
+
label="1"
|
|
258
|
+
onClick={handleClick}
|
|
259
|
+
/>
|
|
260
|
+
</NavigationRail>,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const nav = screen.getByRole("navigation");
|
|
264
|
+
const item = screen.getByRole("menuitem");
|
|
265
|
+
|
|
266
|
+
item.focus();
|
|
267
|
+
fireEvent.keyDown(nav, { key: "Enter" });
|
|
268
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
269
|
+
|
|
270
|
+
fireEvent.keyDown(nav, { key: " " });
|
|
271
|
+
expect(handleClick).toHaveBeenCalledTimes(2);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("renders badge when provided", () => {
|
|
275
|
+
render(
|
|
276
|
+
<NavigationRail>
|
|
277
|
+
<NavigationRailItem
|
|
278
|
+
selected={false}
|
|
279
|
+
icon={<svg />}
|
|
280
|
+
label="Notifications"
|
|
281
|
+
badge="3"
|
|
282
|
+
/>
|
|
283
|
+
</NavigationRail>,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const item = screen.getByRole("menuitem");
|
|
287
|
+
expect(item).toHaveTextContent("3");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// ── labelVisibility tests ──────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
describe("labelVisibility", () => {
|
|
293
|
+
it('shows labels for all items when labelVisibility="labeled" (default)', () => {
|
|
294
|
+
render(
|
|
295
|
+
<NavigationRail labelVisibility="labeled">
|
|
296
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
297
|
+
<NavigationRailItem selected={false} icon={<svg />} label="Search" />
|
|
298
|
+
</NavigationRail>,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
expect(screen.getByText("Home")).toBeInTheDocument();
|
|
302
|
+
expect(screen.getByText("Search")).toBeInTheDocument();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('shows label only for active item when labelVisibility="auto"', () => {
|
|
306
|
+
render(
|
|
307
|
+
<NavigationRail labelVisibility="auto">
|
|
308
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
309
|
+
<NavigationRailItem selected={false} icon={<svg />} label="Search" />
|
|
310
|
+
</NavigationRail>,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
expect(screen.getByText("Home")).toBeInTheDocument();
|
|
314
|
+
expect(screen.queryByText("Search")).not.toBeInTheDocument();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('shows no labels when labelVisibility="unlabeled"', () => {
|
|
318
|
+
render(
|
|
319
|
+
<NavigationRail labelVisibility="unlabeled">
|
|
320
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
321
|
+
<NavigationRailItem selected={false} icon={<svg />} label="Search" />
|
|
322
|
+
</NavigationRail>,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
expect(screen.queryByText("Home")).not.toBeInTheDocument();
|
|
326
|
+
expect(screen.queryByText("Search")).not.toBeInTheDocument();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("always shows labels in expanded variant regardless of labelVisibility", () => {
|
|
330
|
+
render(
|
|
331
|
+
<NavigationRail variant="expanded" labelVisibility="unlabeled">
|
|
332
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
333
|
+
<NavigationRailItem selected={false} icon={<svg />} label="Search" />
|
|
334
|
+
</NavigationRail>,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// In expanded mode, labels are always shown
|
|
338
|
+
expect(screen.getByText("Home")).toBeInTheDocument();
|
|
339
|
+
expect(screen.getByText("Search")).toBeInTheDocument();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// ── Additional Props (xr, narrow, header, footer, fab) ─────────────────────
|
|
344
|
+
|
|
345
|
+
describe("additional layout and xr properties", () => {
|
|
346
|
+
it("renders header, footer, and fab elements", () => {
|
|
347
|
+
render(
|
|
348
|
+
<NavigationRail
|
|
349
|
+
header={<div data-testid="rail-header">Header</div>}
|
|
350
|
+
footer={<div data-testid="rail-footer">Footer</div>}
|
|
351
|
+
fab={
|
|
352
|
+
<button type="button" data-testid="rail-fab">
|
|
353
|
+
FAB
|
|
354
|
+
</button>
|
|
355
|
+
}
|
|
356
|
+
>
|
|
357
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
358
|
+
</NavigationRail>,
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
expect(screen.getByTestId("rail-header")).toBeInTheDocument();
|
|
362
|
+
expect(screen.getByTestId("rail-footer")).toBeInTheDocument();
|
|
363
|
+
expect(screen.getByTestId("rail-fab")).toBeInTheDocument();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("applies narrow styling when narrow={true}", () => {
|
|
367
|
+
render(
|
|
368
|
+
<NavigationRail narrow>
|
|
369
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
370
|
+
</NavigationRail>,
|
|
371
|
+
);
|
|
372
|
+
const nav = screen.getByRole("navigation");
|
|
373
|
+
expect(nav).toHaveClass("w-20"); // narrow width
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("applies xr (spatial) styling when xr={true}", () => {
|
|
377
|
+
render(
|
|
378
|
+
<NavigationRail xr>
|
|
379
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
380
|
+
</NavigationRail>,
|
|
381
|
+
);
|
|
382
|
+
const nav = screen.getByRole("navigation");
|
|
383
|
+
expect(nav).toHaveClass("py-5", "rounded-[48px]", "bg-m3-surface");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("renders spatial wrapper structurally when xr='spatialized'", () => {
|
|
387
|
+
render(
|
|
388
|
+
<NavigationRail
|
|
389
|
+
xr="spatialized"
|
|
390
|
+
fab={
|
|
391
|
+
<button type="button" data-testid="rail-fab">
|
|
392
|
+
FAB
|
|
393
|
+
</button>
|
|
394
|
+
}
|
|
395
|
+
>
|
|
396
|
+
<NavigationRailItem selected icon={<svg />} label="Home" />
|
|
397
|
+
</NavigationRail>,
|
|
398
|
+
);
|
|
399
|
+
expect(screen.getByTestId("rail-fab")).toBeInTheDocument();
|
|
400
|
+
const nav = screen.getByRole("navigation");
|
|
401
|
+
expect(nav).toBeInTheDocument();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|