@sovereignfs/ui 0.54.0 → 0.83.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/dist/Accordion.module.css +1 -1
- package/dist/Alert.module.css +1 -1
- package/dist/AppsLauncher.module.css +96 -0
- package/dist/Avatar.module.css +1 -1
- package/dist/Badge.module.css +30 -7
- package/dist/BalanceChip.module.css +1 -1
- package/dist/Button.module.css +18 -2
- package/dist/Card.module.css +1 -1
- package/dist/CardTile.module.css +181 -0
- package/dist/CodeTextarea.module.css +1 -1
- package/dist/Collapsible.module.css +1 -1
- package/dist/ColorPicker.module.css +132 -0
- package/dist/Combobox.module.css +2 -2
- package/dist/Command.module.css +1 -1
- package/dist/ConfirmDialog.module.css +2 -2
- package/dist/ConsentPrompt.module.css +21 -0
- package/dist/DatePicker.module.css +1 -1
- package/dist/Dialog.module.css +130 -21
- package/dist/Drawer.module.css +36 -7
- package/dist/Header.module.css +62 -0
- package/dist/HeaderFooterLayout.module.css +21 -0
- package/dist/IconPicker.module.css +1 -1
- package/dist/Input.module.css +1 -1
- package/dist/Kbd.module.css +1 -1
- package/dist/Markdown.module.css +24 -0
- package/dist/Menu.module.css +1 -0
- package/dist/Message.module.css +1 -1
- package/dist/MessageScroller.module.css +12 -1
- package/dist/MobileAppsDrawer.module.css +10 -9
- package/dist/MobileFooter.module.css +1 -1
- package/dist/MobileHeader.module.css +14 -1
- package/dist/NavList.module.css +130 -0
- package/dist/NavTabs.module.css +1 -1
- package/dist/NotificationsPanel.module.css +258 -0
- package/dist/OverlayHeader.module.css +1 -1
- package/dist/PageContainer.module.css +62 -0
- package/dist/PageHeader.module.css +54 -0
- package/dist/PageLayout.module.css +51 -0
- package/dist/Popover.module.css +1 -1
- package/dist/QuantityStepper.module.css +1 -1
- package/dist/Resizable.module.css +3 -3
- package/dist/RootLayout.module.css +12 -0
- package/dist/SegmentedControl.module.css +1 -1
- package/dist/Select.module.css +1 -1
- package/dist/Sheet.module.css +4 -1
- package/dist/SplitPane.module.css +4 -4
- package/dist/StatusBadge.module.css +1 -1
- package/dist/StepStrip.module.css +53 -0
- package/dist/SuggestionInput.module.css +2 -2
- package/dist/SwipableMobileCarousel.module.css +12 -3
- package/dist/SwipableMobileCarouselDots.module.css +15 -0
- package/dist/SwipeStack.module.css +101 -0
- package/dist/SystemBanner.module.css +10 -0
- package/dist/Table.module.css +2 -2
- package/dist/Tabs.module.css +1 -1
- package/dist/TagInput.module.css +2 -2
- package/dist/Textarea.module.css +9 -1
- package/dist/ThreeColumnLayout.module.css +35 -0
- package/dist/Toast.module.css +62 -3
- package/dist/Tooltip.module.css +1 -1
- package/dist/UserMenu.module.css +90 -0
- package/dist/index.d.ts +1047 -45
- package/dist/index.js +3908 -1456
- package/dist/tokens/primitives.css +11 -0
- package/dist/tokens/semantic.css +32 -0
- package/dist/tokens/theme-presets.css +33 -0
- package/package.json +25 -9
- package/src/__tests__/package-json.test.ts +8 -0
- package/src/components/Accordion/Accordion.module.css +1 -1
- package/src/components/Alert/Alert.module.css +1 -1
- package/src/components/AppsLauncher/AppsLauncher.module.css +96 -0
- package/src/components/AppsLauncher/AppsLauncher.stories.tsx +58 -0
- package/src/components/AppsLauncher/AppsLauncher.tsx +117 -0
- package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +52 -0
- package/src/components/Avatar/Avatar.module.css +1 -1
- package/src/components/Badge/Badge.module.css +30 -7
- package/src/components/Badge/Badge.stories.tsx +16 -4
- package/src/components/Badge/Badge.tsx +7 -4
- package/src/components/Badge/__tests__/Badge.test.tsx +4 -2
- package/src/components/BalanceChip/BalanceChip.module.css +1 -1
- package/src/components/Button/Button.module.css +18 -2
- package/src/components/Card/Card.module.css +1 -1
- package/src/components/CardTile/CardTile.module.css +181 -0
- package/src/components/CardTile/CardTile.stories.tsx +86 -0
- package/src/components/CardTile/CardTile.tsx +128 -0
- package/src/components/CardTile/__tests__/CardTile.test.tsx +56 -0
- package/src/components/CodeTextarea/CodeTextarea.module.css +1 -1
- package/src/components/Collapsible/Collapsible.module.css +1 -1
- package/src/components/ColorPicker/ColorPicker.module.css +132 -0
- package/src/components/ColorPicker/ColorPicker.stories.tsx +80 -0
- package/src/components/ColorPicker/ColorPicker.tsx +157 -0
- package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +117 -0
- package/src/components/Combobox/Combobox.module.css +2 -2
- package/src/components/Command/Command.module.css +1 -1
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +2 -2
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +28 -2
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +71 -3
- package/src/components/ConsentPrompt/ConsentPrompt.module.css +21 -0
- package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +31 -0
- package/src/components/ConsentPrompt/ConsentPrompt.tsx +66 -0
- package/src/components/ContextMenu/ContextMenu.tsx +1 -1
- package/src/components/DatePicker/DatePicker.module.css +1 -1
- package/src/components/Dialog/Dialog.module.css +130 -21
- package/src/components/Dialog/Dialog.stories.tsx +179 -2
- package/src/components/Dialog/Dialog.tsx +129 -35
- package/src/components/Dialog/__tests__/Dialog.test.tsx +190 -9
- package/src/components/DragHandleRow/DragHandleRow.tsx +2 -21
- package/src/components/Drawer/Drawer.module.css +36 -7
- package/src/components/Drawer/Drawer.tsx +9 -10
- package/src/components/Drawer/__tests__/Drawer.test.tsx +33 -6
- package/src/components/GripIcon/GripIcon.tsx +27 -0
- package/src/components/Header/Header.module.css +62 -0
- package/src/components/Header/Header.stories.tsx +452 -0
- package/src/components/Header/Header.tsx +102 -0
- package/src/components/Header/__tests__/Header.test.tsx +104 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +21 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +111 -0
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +54 -0
- package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +58 -0
- package/src/components/Icon/icons/arrow-left-right.tsx +23 -0
- package/src/components/Icon/icons/bold.tsx +20 -0
- package/src/components/Icon/icons/calendar.tsx +4 -4
- package/src/components/Icon/icons/carrot.tsx +3 -3
- package/src/components/Icon/icons/circle-chevron-left.tsx +21 -0
- package/src/components/Icon/icons/code.tsx +21 -0
- package/src/components/Icon/icons/copy.tsx +21 -0
- package/src/components/Icon/icons/credit-card.tsx +21 -0
- package/src/components/Icon/icons/database.tsx +22 -0
- package/src/components/Icon/icons/download.tsx +22 -0
- package/src/components/Icon/icons/file-text.tsx +24 -0
- package/src/components/Icon/icons/folder-closed.tsx +21 -0
- package/src/components/Icon/icons/folder-open.tsx +20 -0
- package/src/components/Icon/icons/folder.tsx +20 -0
- package/src/components/Icon/icons/folders.tsx +21 -0
- package/src/components/Icon/icons/hat-glasses.tsx +24 -0
- package/src/components/Icon/icons/history.tsx +22 -0
- package/src/components/Icon/icons/inbox.tsx +21 -0
- package/src/components/Icon/icons/index.ts +161 -1
- package/src/components/Icon/icons/italic.tsx +22 -0
- package/src/components/Icon/icons/layers.tsx +22 -0
- package/src/components/Icon/icons/layout-dashboard.tsx +23 -0
- package/src/components/Icon/icons/layout-grid.tsx +23 -0
- package/src/components/Icon/icons/link.tsx +21 -0
- package/src/components/Icon/icons/list-ordered.tsx +25 -0
- package/src/components/Icon/icons/list.tsx +25 -0
- package/src/components/Icon/icons/luggage.tsx +24 -0
- package/src/components/Icon/icons/map-pin.tsx +21 -0
- package/src/components/Icon/icons/minus.tsx +20 -0
- package/src/components/Icon/icons/paint-bucket.tsx +23 -0
- package/src/components/Icon/icons/panel-left.tsx +21 -0
- package/src/components/Icon/icons/pin.tsx +21 -0
- package/src/components/Icon/icons/rectangle-ellipsis.tsx +23 -0
- package/src/components/Icon/icons/refresh-cw.tsx +23 -0
- package/src/components/Icon/icons/rotate-cw.tsx +21 -0
- package/src/components/Icon/icons/route.tsx +22 -0
- package/src/components/Icon/icons/send.tsx +21 -0
- package/src/components/Icon/icons/share-2.tsx +24 -0
- package/src/components/Icon/icons/sheet.tsx +24 -0
- package/src/components/Icon/icons/square-kanban.tsx +23 -0
- package/src/components/Icon/icons/table.tsx +23 -0
- package/src/components/Icon/icons/user-round-plus.tsx +23 -0
- package/src/components/Icon/icons/users.tsx +23 -0
- package/src/components/IconPicker/IconPicker.module.css +1 -1
- package/src/components/Input/Input.module.css +1 -1
- package/src/components/Input/Input.tsx +8 -2
- package/src/components/Kbd/Kbd.module.css +1 -1
- package/src/components/Markdown/Markdown.module.css +24 -0
- package/src/components/Markdown/Markdown.stories.tsx +10 -0
- package/src/components/Markdown/Markdown.tsx +166 -29
- package/src/components/Markdown/__tests__/Markdown.test.tsx +144 -0
- package/src/components/Menu/Menu.module.css +1 -0
- package/src/components/Menu/Menu.tsx +72 -22
- package/src/components/Menu/__tests__/Menu.test.tsx +30 -0
- package/src/components/Menubar/Menubar.tsx +1 -1
- package/src/components/Message/Message.module.css +1 -1
- package/src/components/MessageScroller/MessageScroller.module.css +12 -1
- package/src/components/MessageScroller/MessageScroller.tsx +12 -0
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +10 -9
- package/src/components/MobileFooter/MobileFooter.module.css +1 -1
- package/src/components/MobileFooter/MobileFooter.tsx +15 -2
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +58 -0
- package/src/components/MobileHeader/MobileHeader.module.css +14 -1
- package/src/components/MobileHeader/MobileHeader.tsx +15 -2
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +56 -1
- package/src/components/NavList/NavList.module.css +130 -0
- package/src/components/NavList/NavList.tsx +136 -0
- package/src/components/NavList/__tests__/NavList.test.tsx +109 -0
- package/src/components/NavTabs/NavTabs.module.css +1 -1
- package/src/components/NotificationsPanel/NotificationsPanel.module.css +258 -0
- package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +61 -0
- package/src/components/NotificationsPanel/NotificationsPanel.tsx +187 -0
- package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +78 -0
- package/src/components/OverlayHeader/OverlayHeader.module.css +1 -1
- package/src/components/OverlayHeader/OverlayHeader.tsx +19 -4
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +18 -0
- package/src/components/PageContainer/PageContainer.module.css +62 -0
- package/src/components/PageContainer/PageContainer.tsx +37 -4
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +41 -8
- package/src/components/PageHeader/PageHeader.module.css +54 -0
- package/src/components/PageHeader/PageHeader.tsx +146 -5
- package/src/components/PageHeader/__tests__/PageHeader.test.tsx +103 -0
- package/src/components/PageLayout/PageLayout.module.css +51 -0
- package/src/components/PageLayout/PageLayout.stories.tsx +73 -0
- package/src/components/PageLayout/PageLayout.tsx +51 -0
- package/src/components/PageLayout/__tests__/PageLayout.test.tsx +34 -0
- package/src/components/Popover/Popover.module.css +1 -1
- package/src/components/Popover/Popover.tsx +86 -4
- package/src/components/Popover/__tests__/Popover.test.tsx +165 -0
- package/src/components/QuantityStepper/QuantityStepper.module.css +1 -1
- package/src/components/Resizable/Resizable.module.css +3 -3
- package/src/components/RootLayout/RootLayout.module.css +12 -0
- package/src/components/RootLayout/RootLayout.stories.tsx +125 -0
- package/src/components/RootLayout/RootLayout.tsx +185 -0
- package/src/components/RootLayout/__tests__/RootLayout.test.tsx +161 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +1 -1
- package/src/components/Select/Select.module.css +1 -1
- package/src/components/Sheet/Sheet.module.css +4 -1
- package/src/components/Sheet/Sheet.tsx +5 -5
- package/src/components/SplitPane/SplitPane.module.css +4 -4
- package/src/components/StatusBadge/StatusBadge.module.css +1 -1
- package/src/components/StepStrip/StepStrip.module.css +53 -0
- package/src/components/StepStrip/StepStrip.tsx +74 -0
- package/src/components/SuggestionInput/SuggestionInput.module.css +2 -2
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +12 -3
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +25 -9
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +81 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +15 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +12 -1
- package/src/components/SwipeStack/SwipeStack.module.css +101 -0
- package/src/components/SwipeStack/SwipeStack.tsx +199 -0
- package/src/components/SwipeStack/SwipeStackCard.tsx +22 -0
- package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +169 -0
- package/src/components/SystemBanner/SystemBanner.module.css +10 -0
- package/src/components/Table/Table.module.css +2 -2
- package/src/components/Tabs/Tabs.module.css +1 -1
- package/src/components/TagInput/TagInput.module.css +2 -2
- package/src/components/Textarea/Textarea.module.css +9 -1
- package/src/components/Textarea/Textarea.tsx +28 -5
- package/src/components/Textarea/__tests__/Textarea.test.tsx +23 -1
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +35 -0
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +152 -0
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +87 -0
- package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +78 -0
- package/src/components/Toast/Toast.module.css +62 -3
- package/src/components/Toast/Toast.stories.tsx +1 -1
- package/src/components/Tooltip/Tooltip.module.css +1 -1
- package/src/components/UserMenu/UserMenu.module.css +90 -0
- package/src/components/UserMenu/UserMenu.stories.tsx +49 -0
- package/src/components/UserMenu/UserMenu.tsx +132 -0
- package/src/components/UserMenu/__tests__/UserMenu.test.tsx +77 -0
- package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +118 -0
- package/src/hooks/__tests__/useReorderSensors.test.ts +26 -0
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +82 -0
- package/src/hooks/__tests__/useSwipeStack.test.tsx +155 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/usePublishShellChromeHeight.ts +96 -0
- package/src/hooks/useReorderSensors.ts +88 -0
- package/src/hooks/useSnapCarousel.ts +211 -6
- package/src/hooks/useSwipeStack.ts +235 -0
- package/src/index.ts +61 -1
- package/src/overlay-shell.ts +65 -0
- package/src/stories/DesignSystemOverview.stories.tsx +617 -11
- package/src/stories/GripIcon.stories.tsx +29 -0
- package/src/stories/LayoutSystem.stories.tsx +639 -0
- package/src/stories/MenuEntries.stories.tsx +86 -0
- package/src/stories/NavList.stories.tsx +147 -0
- package/src/stories/PageContainer.stories.tsx +52 -7
- package/src/stories/PageHeader.stories.tsx +54 -1
- package/src/stories/StepStrip.stories.tsx +76 -0
- package/src/stories/SwipableMobileCarousel.stories.tsx +58 -0
- package/src/stories/SwipeStack.stories.tsx +200 -0
- package/src/stories/Textarea.stories.tsx +12 -0
- package/src/stories/TokenGallery.stories.tsx +50 -0
- package/src/tokens/primitives.css +11 -0
- package/src/tokens/semantic.css +32 -0
- package/src/tokens/theme-presets.css +33 -0
- package/src/tokens/theme-presets.ts +72 -0
|
@@ -63,16 +63,24 @@ describe('Drawer', () => {
|
|
|
63
63
|
expect(panel.getAttribute('aria-modal')).toBe('true');
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
it('calls onClose on Escape', () => {
|
|
66
|
+
it('calls onClose exactly once on Escape with focus inside the panel', () => {
|
|
67
67
|
const onClose = vi.fn();
|
|
68
68
|
render(
|
|
69
|
-
<Drawer open onClose={onClose}>
|
|
70
|
-
|
|
69
|
+
<Drawer open onClose={onClose} aria-label="Panel">
|
|
70
|
+
<button>Focus me</button>
|
|
71
71
|
</Drawer>,
|
|
72
72
|
);
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
73
|
+
// Dispatched from the actually-focused element (matching real usage,
|
|
74
|
+
// where useOverlayFocusCapture moves focus into the panel on open) so the
|
|
75
|
+
// keydown bubbles through the scrim on its way to document — the exact
|
|
76
|
+
// path that used to trigger both the scrim's own (now-removed) Escape
|
|
77
|
+
// handler and useOverlayKeyboardTrap's document-level listener, double-
|
|
78
|
+
// firing onClose. Dispatching straight on `document` (the previous form
|
|
79
|
+
// of this test) skips that bubble path and would pass even if the bug
|
|
80
|
+
// were reintroduced.
|
|
81
|
+
const button = screen.getByRole('button', { name: 'Focus me' });
|
|
82
|
+
button.focus();
|
|
83
|
+
fireEvent.keyDown(button, { key: 'Escape' });
|
|
76
84
|
expect(onClose).toHaveBeenCalledOnce();
|
|
77
85
|
});
|
|
78
86
|
|
|
@@ -120,6 +128,25 @@ describe('Drawer', () => {
|
|
|
120
128
|
expect(screen.getAllByRole('button')).toHaveLength(1);
|
|
121
129
|
});
|
|
122
130
|
|
|
131
|
+
it('renders children inside a scrollable body, separate from the grab handle', () => {
|
|
132
|
+
// Regression test: the panel used to be the sole scroll container
|
|
133
|
+
// (overflow-y: auto on .panel itself), so a tall drawer scrolled the
|
|
134
|
+
// grab handle out of view along with the content. The handle must stay
|
|
135
|
+
// a sibling of the scrollable region, not a descendant of it.
|
|
136
|
+
render(
|
|
137
|
+
<Drawer open onClose={() => {}} aria-label="Nav">
|
|
138
|
+
<button type="button">Own action</button>
|
|
139
|
+
</Drawer>,
|
|
140
|
+
);
|
|
141
|
+
const panel = screen.getByRole('dialog');
|
|
142
|
+
const handle = panel.firstElementChild as HTMLElement;
|
|
143
|
+
const button = screen.getByRole('button', { name: 'Own action' });
|
|
144
|
+
const body = button.parentElement as HTMLElement;
|
|
145
|
+
expect(body).not.toBe(panel);
|
|
146
|
+
expect(body.contains(handle)).toBe(false);
|
|
147
|
+
expect(panel.contains(body)).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
123
150
|
it('renders OverlayHeader (title + close) when title is provided', () => {
|
|
124
151
|
const onClose = vi.fn();
|
|
125
152
|
render(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface GripIconProps {
|
|
2
|
+
size?: number;
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** Six-dot drag-handle affordance. No baked-in pointer-events or color —
|
|
7
|
+
* a consumer that needs `pointer-events: none` (e.g. a button wrapping it)
|
|
8
|
+
* or a specific color passes those via `className`/`currentColor`. */
|
|
9
|
+
export function GripIcon({ size = 14, className }: GripIconProps) {
|
|
10
|
+
return (
|
|
11
|
+
<svg
|
|
12
|
+
width={size}
|
|
13
|
+
height={size}
|
|
14
|
+
viewBox="0 0 14 14"
|
|
15
|
+
fill="none"
|
|
16
|
+
aria-hidden="true"
|
|
17
|
+
className={className}
|
|
18
|
+
>
|
|
19
|
+
{/* Two columns of three dots */}
|
|
20
|
+
{[3, 7, 11].map((cy) =>
|
|
21
|
+
[4, 10].map((cx) => (
|
|
22
|
+
<circle key={`${cx}-${cy}`} cx={cx} cy={cy} r={1.2} fill="currentColor" />
|
|
23
|
+
)),
|
|
24
|
+
)}
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.header {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
justify-content: space-between;
|
|
5
|
+
min-height: 48px;
|
|
6
|
+
padding: var(--sv-space-2) var(--sv-space-4);
|
|
7
|
+
border-bottom: var(--sv-border-width-hairline) solid var(--sv-color-border);
|
|
8
|
+
background-color: var(--sv-color-surface);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.left {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--sv-space-2);
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.right {
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
gap: var(--sv-space-1);
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.brandBadge {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
width: 28px;
|
|
31
|
+
height: 28px;
|
|
32
|
+
border-radius: var(--sv-radius-md);
|
|
33
|
+
background-color: var(--sv-color-accent);
|
|
34
|
+
color: var(--sv-color-text-on-accent);
|
|
35
|
+
font-size: var(--sv-font-size-xs);
|
|
36
|
+
font-weight: var(--sv-font-weight-semibold);
|
|
37
|
+
text-decoration: none;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.brandLogoImg {
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
object-fit: cover;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.plugin {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: var(--sv-space-1);
|
|
51
|
+
min-width: 0;
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
color: var(--sv-color-text-primary);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.pluginName {
|
|
57
|
+
font-size: var(--sv-font-size-md);
|
|
58
|
+
font-weight: var(--sv-font-weight-semibold);
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
text-overflow: ellipsis;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Header } from './Header';
|
|
3
|
+
import { MobileHeader } from '../MobileHeader/MobileHeader';
|
|
4
|
+
import { AppsLauncher } from '../AppsLauncher/AppsLauncher';
|
|
5
|
+
import { Icon, type IconName } from '../Icon/Icon';
|
|
6
|
+
import { NotificationsPanel } from '../NotificationsPanel/NotificationsPanel';
|
|
7
|
+
import { UserMenu } from '../UserMenu/UserMenu';
|
|
8
|
+
import { useIsMobile } from '../../hooks';
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Shared doc primitives (mirrors Overview/Mobile Patterns' own doc styling)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
const ff = 'var(--sv-font-family)';
|
|
15
|
+
|
|
16
|
+
function Heading({ level = 2, children }: { level?: 1 | 2 | 3; children: React.ReactNode }) {
|
|
17
|
+
const sizes: Record<number, string> = { 1: '1.75rem', 2: '1.125rem', 3: '0.9375rem' };
|
|
18
|
+
const weights: Record<number, number> = { 1: 700, 2: 600, 3: 600 };
|
|
19
|
+
const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
|
|
20
|
+
return (
|
|
21
|
+
<Tag
|
|
22
|
+
style={{
|
|
23
|
+
fontFamily: ff,
|
|
24
|
+
fontSize: sizes[level],
|
|
25
|
+
fontWeight: weights[level],
|
|
26
|
+
color: 'var(--sv-color-text-primary)',
|
|
27
|
+
margin: 0,
|
|
28
|
+
lineHeight: 1.3,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</Tag>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Body({ children }: { children: React.ReactNode }) {
|
|
37
|
+
return (
|
|
38
|
+
<p
|
|
39
|
+
style={{
|
|
40
|
+
fontFamily: ff,
|
|
41
|
+
fontSize: 'var(--sv-font-size-sm)',
|
|
42
|
+
color: 'var(--sv-color-text-muted)',
|
|
43
|
+
margin: '0.5rem 0 0',
|
|
44
|
+
lineHeight: 1.6,
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</p>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function SectionHeader({ title, subtitle }: { title: string; subtitle?: string }) {
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
style={{
|
|
56
|
+
paddingBottom: 'var(--sv-space-3)',
|
|
57
|
+
borderBottom: '2px solid var(--sv-color-accent)',
|
|
58
|
+
marginBottom: 'var(--sv-space-5)',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<Heading level={2}>{title}</Heading>
|
|
62
|
+
{subtitle && <Body>{subtitle}</Body>}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function Callout({
|
|
68
|
+
type = 'info',
|
|
69
|
+
children,
|
|
70
|
+
}: {
|
|
71
|
+
type?: 'info' | 'warn' | 'tip';
|
|
72
|
+
children: React.ReactNode;
|
|
73
|
+
}) {
|
|
74
|
+
const map = {
|
|
75
|
+
info: {
|
|
76
|
+
bg: 'var(--sv-color-info-surface)',
|
|
77
|
+
border: 'var(--sv-color-info-border)',
|
|
78
|
+
text: 'var(--sv-color-info-text)',
|
|
79
|
+
label: 'ℹ',
|
|
80
|
+
},
|
|
81
|
+
warn: {
|
|
82
|
+
bg: 'var(--sv-color-warning-surface)',
|
|
83
|
+
border: 'var(--sv-color-warning-border)',
|
|
84
|
+
text: 'var(--sv-color-warning-text)',
|
|
85
|
+
label: '⚠',
|
|
86
|
+
},
|
|
87
|
+
tip: {
|
|
88
|
+
bg: 'var(--sv-color-success-surface)',
|
|
89
|
+
border: 'var(--sv-color-success-border)',
|
|
90
|
+
text: 'var(--sv-color-success-text)',
|
|
91
|
+
label: '✓',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const c = map[type];
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
style={{
|
|
98
|
+
background: c.bg,
|
|
99
|
+
border: `1px solid ${c.border}`,
|
|
100
|
+
borderRadius: 'var(--sv-radius-md)',
|
|
101
|
+
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
102
|
+
display: 'flex',
|
|
103
|
+
gap: 'var(--sv-space-2)',
|
|
104
|
+
alignItems: 'flex-start',
|
|
105
|
+
marginBottom: 'var(--sv-space-4)',
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
<span style={{ color: c.text, fontWeight: 700, flexShrink: 0, fontSize: '0.875rem' }}>
|
|
109
|
+
{c.label}
|
|
110
|
+
</span>
|
|
111
|
+
<p
|
|
112
|
+
style={{
|
|
113
|
+
fontFamily: ff,
|
|
114
|
+
fontSize: 'var(--sv-font-size-sm)',
|
|
115
|
+
color: c.text,
|
|
116
|
+
margin: 0,
|
|
117
|
+
lineHeight: 1.55,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{children}
|
|
121
|
+
</p>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function CodeBlock({ children }: { children: string }) {
|
|
127
|
+
return (
|
|
128
|
+
<pre
|
|
129
|
+
style={{
|
|
130
|
+
fontFamily: 'var(--sv-font-family-mono)',
|
|
131
|
+
fontSize: '0.75rem',
|
|
132
|
+
background: 'var(--sv-color-surface-sunken)',
|
|
133
|
+
border: '1px solid var(--sv-color-border)',
|
|
134
|
+
borderRadius: 'var(--sv-radius-md)',
|
|
135
|
+
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
136
|
+
marginTop: 'var(--sv-space-3)',
|
|
137
|
+
overflowX: 'auto',
|
|
138
|
+
color: 'var(--sv-color-text-primary)',
|
|
139
|
+
lineHeight: 1.6,
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
</pre>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function Frame({ children }: { children: React.ReactNode }) {
|
|
148
|
+
return (
|
|
149
|
+
<div
|
|
150
|
+
style={{
|
|
151
|
+
border: '1px solid var(--sv-color-border)',
|
|
152
|
+
borderRadius: 'var(--sv-radius-lg)',
|
|
153
|
+
overflow: 'hidden',
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
{children}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Demo pieces — the real, shared AppsLauncher/NotificationsPanel/UserMenu,
|
|
163
|
+
// not story-local mockups.
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The plugin icon renders at the same 28px as the brand badge next to it,
|
|
168
|
+
* not a smaller inline glyph. `Icon`'s largest built-in size (`lg`, 24px)
|
|
169
|
+
* reads slightly small in a 28px box (lucide-style glyphs carry their own
|
|
170
|
+
* internal inset), so it's scaled up the remaining bit to actually fill the
|
|
171
|
+
* box, the same way a real 28px plugin-icon `<img>` asset would.
|
|
172
|
+
*/
|
|
173
|
+
function HeaderPluginIcon({ name }: { name: IconName }) {
|
|
174
|
+
return (
|
|
175
|
+
<span
|
|
176
|
+
aria-hidden="true"
|
|
177
|
+
style={{
|
|
178
|
+
display: 'inline-flex',
|
|
179
|
+
alignItems: 'center',
|
|
180
|
+
justifyContent: 'center',
|
|
181
|
+
width: 28,
|
|
182
|
+
height: 28,
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
<span style={{ display: 'inline-flex', transform: 'scale(1.15)' }}>
|
|
186
|
+
<Icon name={name} size="lg" aria-hidden />
|
|
187
|
+
</span>
|
|
188
|
+
</span>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const launcherItems = [
|
|
193
|
+
{
|
|
194
|
+
key: 'home',
|
|
195
|
+
icon: <Icon name="house" size="md" aria-hidden />,
|
|
196
|
+
label: 'Home',
|
|
197
|
+
href: '/launcher',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: 'console',
|
|
201
|
+
icon: <Icon name="layout-dashboard" size="md" aria-hidden />,
|
|
202
|
+
label: 'Console',
|
|
203
|
+
href: '/console',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
key: 'notes',
|
|
207
|
+
icon: <Icon name="file" size="md" aria-hidden />,
|
|
208
|
+
label: 'Notes',
|
|
209
|
+
href: '/notes',
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
const notificationItems = [
|
|
214
|
+
{
|
|
215
|
+
id: '1',
|
|
216
|
+
icon: <Icon name="layers" size="sm" aria-hidden />,
|
|
217
|
+
title: 'Added to a project',
|
|
218
|
+
timeLabel: '2d ago',
|
|
219
|
+
read: false,
|
|
220
|
+
onDismiss: () => {},
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const accountItems = [
|
|
225
|
+
{ label: 'Account', icon: 'user' as const, href: '/account' },
|
|
226
|
+
{ label: 'Preferences', icon: 'sliders-horizontal' as const, href: '/account/preferences' },
|
|
227
|
+
{ label: 'Sign out', icon: 'log-out' as const, destructive: true, onSelect: () => {} },
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
function Launcher() {
|
|
231
|
+
return <AppsLauncher items={launcherItems} />;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function Notifications() {
|
|
235
|
+
return <NotificationsPanel items={notificationItems} unreadCount={1} />;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function Account() {
|
|
239
|
+
return <UserMenu name="Kasun Benthara" email="kasun@openfs.io" items={accountItems} size="sm" />;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* `Header` is web-only (`shell: minimal`'s desktop top bar) — a plugin
|
|
244
|
+
* renders its own mobile equivalent alongside it, gated with `useIsMobile`
|
|
245
|
+
* (not CSS, to avoid mounting the wrong chrome's hooks on the wrong
|
|
246
|
+
* surface). Resize the Storybook viewport below 768px to see `MobileHeader`
|
|
247
|
+
* render instead. The launcher/notifications/avatar triggers are the real,
|
|
248
|
+
* shared `AppsLauncher`/`NotificationsPanel`/`UserMenu` components — shared
|
|
249
|
+
* verbatim across both branches, not two implementations of the same
|
|
250
|
+
* dropdown, and fully clickable here.
|
|
251
|
+
*/
|
|
252
|
+
function ResponsiveHeaderDemo() {
|
|
253
|
+
const isMobile = useIsMobile();
|
|
254
|
+
|
|
255
|
+
if (isMobile) {
|
|
256
|
+
return <MobileHeader title="Sovereign" bell={<Notifications />} avatarMenu={<Account />} />;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<Header
|
|
261
|
+
instanceName="Sovereign"
|
|
262
|
+
homeHref="/launcher"
|
|
263
|
+
plugin={{
|
|
264
|
+
name: 'Notes',
|
|
265
|
+
icon: <HeaderPluginIcon name="file" />,
|
|
266
|
+
href: '/notes',
|
|
267
|
+
}}
|
|
268
|
+
launcher={<Launcher />}
|
|
269
|
+
notifications={<Notifications />}
|
|
270
|
+
avatarMenu={<Account />}
|
|
271
|
+
/>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// Default — the documentation page
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
function HeaderDoc() {
|
|
280
|
+
return (
|
|
281
|
+
<div
|
|
282
|
+
style={{
|
|
283
|
+
padding: 'var(--sv-space-6)',
|
|
284
|
+
background: 'var(--sv-color-surface)',
|
|
285
|
+
minHeight: '100vh',
|
|
286
|
+
fontFamily: ff,
|
|
287
|
+
maxWidth: 760,
|
|
288
|
+
margin: '0 auto',
|
|
289
|
+
}}
|
|
290
|
+
>
|
|
291
|
+
<div style={{ marginBottom: 'var(--sv-space-8)' }}>
|
|
292
|
+
<Heading level={1}>Header</Heading>
|
|
293
|
+
<Body>
|
|
294
|
+
The web top-bar header for <code>shell: minimal</code> plugins — a plugin that owns its
|
|
295
|
+
whole viewport (no platform sidebar) renders this instead, so it doesn't have to hand-roll
|
|
296
|
+
its own top bar from scratch.
|
|
297
|
+
</Body>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
301
|
+
<SectionHeader
|
|
302
|
+
title="Live demo"
|
|
303
|
+
subtitle="Real, clickable components — not mockups. Resize the Storybook viewport below 768px to see MobileHeader render instead of a squeezed desktop bar."
|
|
304
|
+
/>
|
|
305
|
+
<Frame>
|
|
306
|
+
<ResponsiveHeaderDemo />
|
|
307
|
+
</Frame>
|
|
308
|
+
</section>
|
|
309
|
+
|
|
310
|
+
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
311
|
+
<SectionHeader
|
|
312
|
+
title="Anatomy"
|
|
313
|
+
subtitle="Brand badge + optional active plugin on the left; launcher, notifications, and avatar menu on the right — always rendered, never optional."
|
|
314
|
+
/>
|
|
315
|
+
<ul style={{ margin: 0, paddingLeft: '1.25rem', color: 'var(--sv-color-text-muted)' }}>
|
|
316
|
+
<li>
|
|
317
|
+
<strong style={{ color: 'var(--sv-color-text-primary)' }}>Brand badge</strong> — derives
|
|
318
|
+
its initial from <code>instanceName</code>, links to <code>homeHref</code>.
|
|
319
|
+
</li>
|
|
320
|
+
<li>
|
|
321
|
+
<strong style={{ color: 'var(--sv-color-text-primary)' }}>Active plugin</strong> (
|
|
322
|
+
<code>plugin</code>, optional) — icon + name, links to <code>plugin.href</code>.
|
|
323
|
+
</li>
|
|
324
|
+
<li>
|
|
325
|
+
<strong style={{ color: 'var(--sv-color-text-primary)' }}>launcher</strong> — the real{' '}
|
|
326
|
+
<code>AppsLauncher</code>.
|
|
327
|
+
</li>
|
|
328
|
+
<li>
|
|
329
|
+
<strong style={{ color: 'var(--sv-color-text-primary)' }}>notifications</strong> — the
|
|
330
|
+
real <code>NotificationsPanel</code>.
|
|
331
|
+
</li>
|
|
332
|
+
<li>
|
|
333
|
+
<strong style={{ color: 'var(--sv-color-text-primary)' }}>avatarMenu</strong> — the real{' '}
|
|
334
|
+
<code>UserMenu</code> (<code>size="sm"</code> in this context — see Sizing
|
|
335
|
+
below).
|
|
336
|
+
</li>
|
|
337
|
+
</ul>
|
|
338
|
+
<CodeBlock>{`<Header
|
|
339
|
+
instanceName="Sovereign"
|
|
340
|
+
homeHref="/launcher"
|
|
341
|
+
plugin={{ name: 'Notes', icon: <PluginIcon />, href: '/notes' }}
|
|
342
|
+
launcher={<AppsLauncher items={apps} />}
|
|
343
|
+
notifications={<NotificationsPanel items={items} unreadCount={n} />}
|
|
344
|
+
avatarMenu={<UserMenu name={user.name} email={user.email} items={menuItems} size="sm" />}
|
|
345
|
+
/>`}</CodeBlock>
|
|
346
|
+
</section>
|
|
347
|
+
|
|
348
|
+
<section style={{ marginBottom: 'var(--sv-space-10)' }}>
|
|
349
|
+
<SectionHeader title="Boundary — presentational only" />
|
|
350
|
+
<Body>
|
|
351
|
+
<code>Header</code> owns layout only — never launcher/notification/account interaction
|
|
352
|
+
logic, matching <code>MobileHeader</code>'s own boundary (RFC 0088).{' '}
|
|
353
|
+
<code>launcher</code>/<code>notifications</code>/<code>avatarMenu</code> are supplied
|
|
354
|
+
fully built by the consumer. In practice that consumer is almost always the real{' '}
|
|
355
|
+
<code>AppsLauncher</code>/<code>NotificationsPanel</code>/<code>UserMenu</code> — see
|
|
356
|
+
their own Components pages — not a bespoke reimplementation.
|
|
357
|
+
</Body>
|
|
358
|
+
</section>
|
|
359
|
+
|
|
360
|
+
<section style={{ marginBottom: 'var(--sv-space-6)' }}>
|
|
361
|
+
<SectionHeader
|
|
362
|
+
title="Sizing"
|
|
363
|
+
subtitle="A compact top bar, deliberately smaller than the platform sidebar — this plugin owns its whole viewport, so it isn't bound to platform chrome dimensions."
|
|
364
|
+
/>
|
|
365
|
+
<ul style={{ margin: 0, paddingLeft: '1.25rem', color: 'var(--sv-color-text-muted)' }}>
|
|
366
|
+
<li>Bar height: 49px (48px content + 1px border).</li>
|
|
367
|
+
<li>Brand badge / plugin icon: 28px.</li>
|
|
368
|
+
<li>Launcher / notifications trigger buttons: 28px, with a 20–24px icon inside.</li>
|
|
369
|
+
<li>
|
|
370
|
+
Avatar: 32px here (<code>UserMenu size="sm"</code>) — smaller than the
|
|
371
|
+
platform sidebar's own 36px avatar (<code>UserMenu</code>'s default), to suit
|
|
372
|
+
this compact top bar's own proportions.
|
|
373
|
+
</li>
|
|
374
|
+
</ul>
|
|
375
|
+
<Callout type="tip">
|
|
376
|
+
Want to try different <code>instanceName</code>/<code>homeHref</code>/<code>plugin</code>{' '}
|
|
377
|
+
values live? See the <strong>Playground</strong> story — its Controls panel is wired up
|
|
378
|
+
for exactly that.
|
|
379
|
+
</Callout>
|
|
380
|
+
</section>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
// Story
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
const meta = {
|
|
390
|
+
title: 'Components/Header',
|
|
391
|
+
component: Header,
|
|
392
|
+
parameters: {
|
|
393
|
+
// fullscreen, not padded — this is a full-bleed top bar; Storybook's own
|
|
394
|
+
// canvas gutter around a `padded` story reads as extra height in a
|
|
395
|
+
// screenshot comparison against the real, edge-to-edge running app. The
|
|
396
|
+
// Default doc page supplies its own centered/padded wrapper internally.
|
|
397
|
+
layout: 'fullscreen',
|
|
398
|
+
docs: {
|
|
399
|
+
description: {
|
|
400
|
+
component:
|
|
401
|
+
'The web top-bar header for shell: minimal plugins — the counterpart to the platform sidebar for a plugin that owns its whole viewport. Brand badge and optional active plugin on the left; launcher (AppsLauncher), notifications (NotificationsPanel), and avatar menu (UserMenu) on the right. Below 768px, plugins render MobileHeader instead.',
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
argTypes: {
|
|
406
|
+
instanceName: { control: 'text' },
|
|
407
|
+
homeHref: { control: 'text' },
|
|
408
|
+
plugin: { control: 'object' },
|
|
409
|
+
launcher: { control: false },
|
|
410
|
+
notifications: { control: false },
|
|
411
|
+
avatarMenu: { control: false },
|
|
412
|
+
},
|
|
413
|
+
args: {
|
|
414
|
+
instanceName: 'Sovereign',
|
|
415
|
+
homeHref: '/launcher',
|
|
416
|
+
plugin: {
|
|
417
|
+
name: 'Notes',
|
|
418
|
+
icon: <HeaderPluginIcon name="file" />,
|
|
419
|
+
href: '/notes',
|
|
420
|
+
},
|
|
421
|
+
launcher: <Launcher />,
|
|
422
|
+
notifications: <Notifications />,
|
|
423
|
+
avatarMenu: <Account />,
|
|
424
|
+
},
|
|
425
|
+
} satisfies Meta<typeof Header>;
|
|
426
|
+
|
|
427
|
+
export default meta;
|
|
428
|
+
type StoryDetail = StoryObj<typeof meta>;
|
|
429
|
+
|
|
430
|
+
export const Story: StoryDetail = {
|
|
431
|
+
parameters: {
|
|
432
|
+
controls: { disable: true },
|
|
433
|
+
},
|
|
434
|
+
render: () => <HeaderDoc />,
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* A plain, bare `Header` — use the Controls panel below to try different
|
|
439
|
+
* `instanceName`/`homeHref`/`plugin` values live. `launcher`/`notifications`/
|
|
440
|
+
* `avatarMenu` stay fixed to the real shared components (they're ReactNode
|
|
441
|
+
* slots — not meaningfully editable as Controls). `plugin.icon` is omitted
|
|
442
|
+
* here (unlike the live demo above) so the object control stays plain,
|
|
443
|
+
* editable JSON rather than a React element it can't serialize.
|
|
444
|
+
*/
|
|
445
|
+
export const Playground: StoryDetail = {
|
|
446
|
+
args: {
|
|
447
|
+
plugin: {
|
|
448
|
+
name: 'Notes',
|
|
449
|
+
href: '/notes',
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
};
|