@xsolla/xui-b2b-sidebar 0.158.0 → 0.160.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/README.md +101 -77
- package/native/index.d.mts +19 -0
- package/native/index.d.ts +19 -0
- package/native/index.js +99 -2
- package/native/index.js.map +1 -1
- package/native/index.mjs +99 -2
- package/native/index.mjs.map +1 -1
- package/package.json +6 -6
- package/web/index.d.mts +19 -0
- package/web/index.d.ts +19 -0
- package/web/index.js +99 -2
- package/web/index.js.map +1 -1
- package/web/index.mjs +99 -2
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
type SidebarLinkProps,
|
|
35
35
|
type SidebarLinkActiveCheck,
|
|
36
36
|
type SidebarLinkClickHandler,
|
|
37
|
-
} from
|
|
37
|
+
} from "@xsolla/xui-b2b-sidebar";
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Quick start
|
|
@@ -42,8 +42,8 @@ import {
|
|
|
42
42
|
Wrap the sidebar in `SidebarProvider`, supplying the current `pathname`, the controlled `collapsed` state, and your app's link component (React Router, Next.js `Link`, etc.). The default link is a plain `<a>` if you omit `linkComponent`.
|
|
43
43
|
|
|
44
44
|
```tsx
|
|
45
|
-
import { useState } from
|
|
46
|
-
import { Home, Wallet, Settings } from
|
|
45
|
+
import { useState } from "react";
|
|
46
|
+
import { Home, Wallet, Settings } from "@xsolla/xui-icons-base";
|
|
47
47
|
import {
|
|
48
48
|
Sidebar,
|
|
49
49
|
SidebarProvider,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
SidebarMenuCollapsible,
|
|
56
56
|
SidebarMenuSub,
|
|
57
57
|
SidebarTrigger,
|
|
58
|
-
} from
|
|
58
|
+
} from "@xsolla/xui-b2b-sidebar";
|
|
59
59
|
|
|
60
60
|
function AppShell({ pathname }: { pathname: string }) {
|
|
61
61
|
const [collapsed, setCollapsed] = useState(false);
|
|
@@ -78,11 +78,19 @@ function AppShell({ pathname }: { pathname: string }) {
|
|
|
78
78
|
<SidebarMenuCollapsible
|
|
79
79
|
icon={<Wallet size={18} variant="line" aria-hidden />}
|
|
80
80
|
label="Finance"
|
|
81
|
-
matchPaths={[
|
|
81
|
+
matchPaths={["/finance"]}
|
|
82
82
|
>
|
|
83
83
|
<SidebarMenuSub>
|
|
84
|
-
<SidebarMenuItem
|
|
85
|
-
|
|
84
|
+
<SidebarMenuItem
|
|
85
|
+
to="/finance/payouts"
|
|
86
|
+
label="Payouts"
|
|
87
|
+
isNested
|
|
88
|
+
/>
|
|
89
|
+
<SidebarMenuItem
|
|
90
|
+
to="/finance/reports"
|
|
91
|
+
label="Reports"
|
|
92
|
+
isNested
|
|
93
|
+
/>
|
|
86
94
|
</SidebarMenuSub>
|
|
87
95
|
</SidebarMenuCollapsible>
|
|
88
96
|
</SidebarMenu>
|
|
@@ -112,13 +120,14 @@ None of the sidebar components extend `ThemeOverrideProps` — they read theme v
|
|
|
112
120
|
|
|
113
121
|
### `<SidebarProvider>`
|
|
114
122
|
|
|
115
|
-
| Prop
|
|
116
|
-
|
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
123
|
+
| Prop | Type | Default | Description |
|
|
124
|
+
| ------------------- | --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| `testID` | `string` | — | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
|
|
126
|
+
| `collapsed` | `boolean` | uncontrolled | Controlled collapsed state. Omit to use the internal state. |
|
|
127
|
+
| `onCollapsedChange` | `(collapsed: boolean) => void` | — | Called when the user toggles via `SidebarTrigger` or the collapsed-mode toggle. |
|
|
128
|
+
| `pathname` | `string` | `""` | Current route, used for active-state matching and `matchPaths` auto-expand. |
|
|
129
|
+
| `linkComponent` | `ComponentType<SidebarLinkProps>` | plain `<a>` | Router link component. |
|
|
130
|
+
| `children` | `ReactNode` | — | Sidebar tree. |
|
|
122
131
|
|
|
123
132
|
### `useSidebar()`
|
|
124
133
|
|
|
@@ -139,61 +148,61 @@ function useSidebar(): {
|
|
|
139
148
|
|
|
140
149
|
### `<Sidebar>`
|
|
141
150
|
|
|
142
|
-
| Prop
|
|
143
|
-
|
|
|
144
|
-
| `collapsedItems`
|
|
145
|
-
| `collapsedToolItems`
|
|
146
|
-
| `collapsedBottomItems` | `SidebarItemType[]` | `[]`
|
|
147
|
-
| `showChat`
|
|
148
|
-
| `onChatClick`
|
|
149
|
-
| `chatBadge`
|
|
150
|
-
| `children`
|
|
151
|
+
| Prop | Type | Default | Description |
|
|
152
|
+
| ---------------------- | ------------------- | ------- | ------------------------------------------------------ |
|
|
153
|
+
| `collapsedItems` | `SidebarItemType[]` | `[]` | Top icons in the collapsed strip. |
|
|
154
|
+
| `collapsedToolItems` | `SidebarItemType[]` | `[]` | Tool icons rendered after a spacer. |
|
|
155
|
+
| `collapsedBottomItems` | `SidebarItemType[]` | `[]` | Bottom icons (e.g. Settings, Billing). |
|
|
156
|
+
| `showChat` | `boolean` | `true` | Render the chat button in the collapsed footer. |
|
|
157
|
+
| `onChatClick` | `() => void` | — | Click handler for the chat button. |
|
|
158
|
+
| `chatBadge` | `boolean` | `false` | Show an unread-indicator dot on the chat button. |
|
|
159
|
+
| `children` | `ReactNode` | — | The expanded tree (`SidebarContent`, `SidebarFooter`). |
|
|
151
160
|
|
|
152
161
|
### `<SidebarMenuItem>`
|
|
153
162
|
|
|
154
|
-
| Prop
|
|
155
|
-
|
|
|
156
|
-
| `to`
|
|
157
|
-
| `label`
|
|
158
|
-
| `icon`
|
|
159
|
-
| `exact`
|
|
160
|
-
| `external`
|
|
161
|
-
| `hasExternalIcon` | `boolean`
|
|
162
|
-
| `target`
|
|
163
|
-
| `onClick`
|
|
164
|
-
| `dataId`
|
|
165
|
-
| `isActive`
|
|
166
|
-
| `isPinned`
|
|
167
|
-
| `showBadge`
|
|
168
|
-
| `hasTooltip`
|
|
169
|
-
| `beta`
|
|
170
|
-
| `multiLine`
|
|
171
|
-
| `extra`
|
|
172
|
-
| `isNested`
|
|
163
|
+
| Prop | Type | Default | Description |
|
|
164
|
+
| ----------------- | ------------------------- | ------- | ----------------------------------------------------------------------------------- |
|
|
165
|
+
| `to` | `string` | — | Route URL passed through to `linkComponent`. |
|
|
166
|
+
| `label` | `ReactNode` | — | Text label. |
|
|
167
|
+
| `icon` | `ReactNode` | — | Leading icon (hidden when `isNested`). |
|
|
168
|
+
| `exact` | `boolean` | — | Exact-match flag forwarded to the link component. |
|
|
169
|
+
| `external` | `boolean` | — | Marks the link as external (sets `target`/`rel`). |
|
|
170
|
+
| `hasExternalIcon` | `boolean` | — | Shows the external-link icon at the trailing edge. |
|
|
171
|
+
| `target` | `string \| null` | — | Anchor target. |
|
|
172
|
+
| `onClick` | `SidebarLinkClickHandler` | — | Click handler. |
|
|
173
|
+
| `dataId` | `string` | — | `data-id` attribute for tests/analytics. |
|
|
174
|
+
| `isActive` | `SidebarLinkActiveCheck` | — | Custom active-check forwarded to the link. |
|
|
175
|
+
| `isPinned` | `boolean` | — | Renders the icon at 12px to indicate a pinned item. |
|
|
176
|
+
| `showBadge` | `boolean` | — | Shows a small alert dot next to the label. |
|
|
177
|
+
| `hasTooltip` | `boolean` | — | Truncates labels longer than 20 chars and shows the full text on hover. |
|
|
178
|
+
| `beta` | `boolean` | — | Renders a "Beta" tag at the trailing edge. |
|
|
179
|
+
| `multiLine` | `boolean` | — | Allows the label to wrap to multiple lines. |
|
|
180
|
+
| `extra` | `ReactNode` | — | Slot rendered between the icon and the label. |
|
|
181
|
+
| `isNested` | `boolean` | `false` | Set when rendered inside `SidebarMenuSub` (drops the icon, indents under the rail). |
|
|
173
182
|
|
|
174
183
|
### `<SidebarMenuCollapsible>`
|
|
175
184
|
|
|
176
|
-
| Prop
|
|
177
|
-
|
|
|
178
|
-
| `icon`
|
|
179
|
-
| `label`
|
|
180
|
-
| `dataId`
|
|
181
|
-
| `matchPaths` | `string[]`
|
|
182
|
-
| `children`
|
|
185
|
+
| Prop | Type | Default | Description |
|
|
186
|
+
| ------------ | ----------- | ------- | ------------------------------------------------------------------------- |
|
|
187
|
+
| `icon` | `ReactNode` | — | Leading icon. |
|
|
188
|
+
| `label` | `ReactNode` | — | Section label. |
|
|
189
|
+
| `dataId` | `string` | — | `data-id` attribute. Also seeds the stable id used by `aria-controls`. |
|
|
190
|
+
| `matchPaths` | `string[]` | `[]` | Route prefixes that auto-expand this section. |
|
|
191
|
+
| `children` | `ReactNode` | — | Nested items, typically a `SidebarMenuSub` of `SidebarMenuItem isNested`. |
|
|
183
192
|
|
|
184
193
|
### `<SidebarCollapsed>`
|
|
185
194
|
|
|
186
195
|
Lower-level collapsed-strip renderer. The `<Sidebar>` component composes this for you; reach for it when you need to render the collapsed strip outside the standard layout.
|
|
187
196
|
|
|
188
|
-
| Prop
|
|
189
|
-
|
|
|
190
|
-
| `items`
|
|
191
|
-
| `toolItems`
|
|
192
|
-
| `bottomItems`
|
|
193
|
-
| `onToggleCollapse` | `() => void`
|
|
194
|
-
| `onChatClick`
|
|
195
|
-
| `showChat`
|
|
196
|
-
| `chatBadge`
|
|
197
|
+
| Prop | Type | Default | Description |
|
|
198
|
+
| ------------------ | ------------------- | ------- | -------------------------------------------------- |
|
|
199
|
+
| `items` | `SidebarItemType[]` | — | Top icon items. |
|
|
200
|
+
| `toolItems` | `SidebarItemType[]` | `[]` | Tool icons after a spacer. |
|
|
201
|
+
| `bottomItems` | `SidebarItemType[]` | `[]` | Bottom icons. |
|
|
202
|
+
| `onToggleCollapse` | `() => void` | — | Click handler for the toggle button in the footer. |
|
|
203
|
+
| `onChatClick` | `() => void` | — | Click handler for the chat button. |
|
|
204
|
+
| `showChat` | `boolean` | `true` | Render the chat button. |
|
|
205
|
+
| `chatBadge` | `boolean` | `false` | Show the unread dot. |
|
|
197
206
|
|
|
198
207
|
### `<SidebarContent>`
|
|
199
208
|
|
|
@@ -241,7 +250,7 @@ type SidebarItemType = {
|
|
|
241
250
|
multiLine?: boolean;
|
|
242
251
|
isPinned?: boolean;
|
|
243
252
|
showBadge?: boolean;
|
|
244
|
-
reverse?: boolean;
|
|
253
|
+
reverse?: boolean; // reserved for legacy reversed-layout items
|
|
245
254
|
isActive?: SidebarLinkActiveCheck;
|
|
246
255
|
children?: SidebarItemType[];
|
|
247
256
|
privileges?: string[];
|
|
@@ -260,6 +269,7 @@ interface SidebarLinkProps {
|
|
|
260
269
|
isActive?: SidebarLinkActiveCheck;
|
|
261
270
|
onClick?: SidebarLinkClickHandler;
|
|
262
271
|
dataId?: string;
|
|
272
|
+
"aria-label"?: string;
|
|
263
273
|
children: ReactNode;
|
|
264
274
|
}
|
|
265
275
|
|
|
@@ -278,8 +288,8 @@ type SidebarLinkClickHandler = (event?: React.MouseEvent<HTMLElement>) => void;
|
|
|
278
288
|
Supply your router's link via the `linkComponent` prop on `SidebarProvider`. The component receives `to`, `exact`, `external`, `target`, `className`, `activeClassName`, `isActive`, `onClick`, `dataId`, and renders children.
|
|
279
289
|
|
|
280
290
|
```tsx
|
|
281
|
-
import { NavLink } from
|
|
282
|
-
import type { SidebarLinkProps } from
|
|
291
|
+
import { NavLink } from "react-router-dom";
|
|
292
|
+
import type { SidebarLinkProps } from "@xsolla/xui-b2b-sidebar";
|
|
283
293
|
|
|
284
294
|
const RouterLink: React.FC<SidebarLinkProps> = ({
|
|
285
295
|
to,
|
|
@@ -291,9 +301,11 @@ const RouterLink: React.FC<SidebarLinkProps> = ({
|
|
|
291
301
|
dataId,
|
|
292
302
|
}) => (
|
|
293
303
|
<NavLink
|
|
294
|
-
to={to ??
|
|
304
|
+
to={to ?? "#"}
|
|
295
305
|
end={exact}
|
|
296
|
-
className={({ isActive }) =>
|
|
306
|
+
className={({ isActive }) =>
|
|
307
|
+
[className, isActive && activeClassName].filter(Boolean).join(" ")
|
|
308
|
+
}
|
|
297
309
|
data-id={dataId}
|
|
298
310
|
onClick={onClick}
|
|
299
311
|
>
|
|
@@ -311,13 +323,17 @@ const RouterLink: React.FC<SidebarLinkProps> = ({
|
|
|
311
323
|
Pass `matchPaths` to `SidebarMenuCollapsible`. When the current `pathname` starts with any of the listed prefixes, the section auto-expands. Only one collapsible can be open at a time, so navigating between sections collapses the others.
|
|
312
324
|
|
|
313
325
|
```tsx
|
|
314
|
-
import { Wallet } from
|
|
315
|
-
import {
|
|
326
|
+
import { Wallet } from "@xsolla/xui-icons-base";
|
|
327
|
+
import {
|
|
328
|
+
SidebarMenuCollapsible,
|
|
329
|
+
SidebarMenuSub,
|
|
330
|
+
SidebarMenuItem,
|
|
331
|
+
} from "@xsolla/xui-b2b-sidebar";
|
|
316
332
|
|
|
317
333
|
<SidebarMenuCollapsible
|
|
318
334
|
icon={<Wallet size={18} variant="line" aria-hidden />}
|
|
319
335
|
label="Finance"
|
|
320
|
-
matchPaths={[
|
|
336
|
+
matchPaths={["/finance", "/finance/payouts", "/finance/reports"]}
|
|
321
337
|
>
|
|
322
338
|
<SidebarMenuSub>
|
|
323
339
|
<SidebarMenuItem to="/finance/payouts" label="Payouts" isNested />
|
|
@@ -329,8 +345,8 @@ import { SidebarMenuCollapsible, SidebarMenuSub, SidebarMenuItem } from '@xsolla
|
|
|
329
345
|
### Item flags
|
|
330
346
|
|
|
331
347
|
```tsx
|
|
332
|
-
import { Pin, Graph, Layer } from
|
|
333
|
-
import { SidebarMenuItem } from
|
|
348
|
+
import { Pin, Graph, Layer } from "@xsolla/xui-icons-base";
|
|
349
|
+
import { SidebarMenuItem } from "@xsolla/xui-b2b-sidebar";
|
|
334
350
|
|
|
335
351
|
<>
|
|
336
352
|
<SidebarMenuItem
|
|
@@ -359,20 +375,24 @@ import { SidebarMenuItem } from '@xsolla/xui-b2b-sidebar';
|
|
|
359
375
|
|
|
360
376
|
### Collapsed icon strip
|
|
361
377
|
|
|
362
|
-
Because the collapsed layout is fundamentally different (icons only, hover popovers), pass icon-strip items separately as `collapsedItems`, `collapsedToolItems` (rendered after a spacer in the main scroll area), and `collapsedBottomItems` (rendered above the chat/toggle footer). Items with `children` open a hover popover listing the children; items without children
|
|
378
|
+
Because the collapsed layout is fundamentally different (icons only, hover popovers), pass icon-strip items separately as `collapsedItems`, `collapsedToolItems` (rendered after a spacer in the main scroll area), and `collapsedBottomItems` (rendered above the chat/toggle footer). Items with `children` open a hover popover listing the children; items without children navigate directly on click (the icon is rendered as a link), and show a label tooltip on hover for discoverability.
|
|
363
379
|
|
|
364
380
|
```tsx
|
|
365
|
-
import { Home, Wallet, Settings } from
|
|
366
|
-
import { Sidebar, type SidebarItemType } from
|
|
381
|
+
import { Home, Wallet, Settings } from "@xsolla/xui-icons-base";
|
|
382
|
+
import { Sidebar, type SidebarItemType } from "@xsolla/xui-b2b-sidebar";
|
|
367
383
|
|
|
368
384
|
const mainNavItems: SidebarItemType[] = [
|
|
369
|
-
{ to: '/dashboard', label: 'Dashboard', icon: <Home size={18} variant="line" aria-hidden /> },
|
|
370
385
|
{
|
|
371
|
-
|
|
386
|
+
to: "/dashboard",
|
|
387
|
+
label: "Dashboard",
|
|
388
|
+
icon: <Home size={18} variant="line" aria-hidden />,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
label: "Finance",
|
|
372
392
|
icon: <Wallet size={18} variant="line" aria-hidden />,
|
|
373
393
|
children: [
|
|
374
|
-
{ to:
|
|
375
|
-
{ to:
|
|
394
|
+
{ to: "/finance/payouts", label: "Payouts" },
|
|
395
|
+
{ to: "/finance/reports", label: "Reports" },
|
|
376
396
|
],
|
|
377
397
|
},
|
|
378
398
|
];
|
|
@@ -380,7 +400,11 @@ const mainNavItems: SidebarItemType[] = [
|
|
|
380
400
|
<Sidebar
|
|
381
401
|
collapsedItems={mainNavItems}
|
|
382
402
|
collapsedBottomItems={[
|
|
383
|
-
{
|
|
403
|
+
{
|
|
404
|
+
to: "/settings",
|
|
405
|
+
label: "Settings",
|
|
406
|
+
icon: <Settings size={18} aria-hidden />,
|
|
407
|
+
},
|
|
384
408
|
]}
|
|
385
409
|
onChatClick={() => openSupportChat()}
|
|
386
410
|
chatBadge={hasUnreadMessages}
|
|
@@ -394,7 +418,7 @@ const mainNavItems: SidebarItemType[] = [
|
|
|
394
418
|
- The root sidebar is `role="navigation"` with `aria-label="Sidebar navigation"`. The expanded and collapsed panes are toggled via `aria-hidden` so only one is announced at a time.
|
|
395
419
|
- `SidebarTrigger` and the collapsed-mode toggle have `aria-pressed` and a context-aware `aria-label` (`"Expand sidebar"` / `"Collapse sidebar"`).
|
|
396
420
|
- Each `SidebarMenuCollapsible` header is a `<button>` with `aria-expanded` and `aria-controls` linking to its region.
|
|
397
|
-
- Collapsed icon items expose `aria-haspopup="menu"
|
|
421
|
+
- Collapsed icon items with children expose `aria-haspopup="menu"`; their popovers are keyboard-reachable (focus opens, blur closes after 150ms, Escape closes immediately). Leaf items (no children) render as native links and navigate on click or Enter.
|
|
398
422
|
- Truncated labels (`hasTooltip`) fall back to a Tooltip with the full text.
|
|
399
423
|
|
|
400
424
|
## Behaviour
|
package/native/index.d.mts
CHANGED
|
@@ -16,7 +16,10 @@ interface SidebarLinkProps {
|
|
|
16
16
|
isActive?: SidebarLinkActiveCheck;
|
|
17
17
|
onClick?: SidebarLinkClickHandler;
|
|
18
18
|
dataId?: string;
|
|
19
|
+
"aria-label"?: string;
|
|
19
20
|
children: ReactNode;
|
|
21
|
+
/** Test ID for testing frameworks */
|
|
22
|
+
testID?: string;
|
|
20
23
|
}
|
|
21
24
|
interface SidebarContextValue {
|
|
22
25
|
collapsed: boolean;
|
|
@@ -32,6 +35,8 @@ interface SidebarProviderProps {
|
|
|
32
35
|
pathname?: string;
|
|
33
36
|
linkComponent?: React.ComponentType<SidebarLinkProps>;
|
|
34
37
|
children: ReactNode;
|
|
38
|
+
/** Test ID for testing frameworks */
|
|
39
|
+
testID?: string;
|
|
35
40
|
}
|
|
36
41
|
declare const SidebarProvider: React.FC<SidebarProviderProps>;
|
|
37
42
|
declare const useSidebar: () => SidebarContextValue;
|
|
@@ -104,6 +109,8 @@ interface SidebarProps {
|
|
|
104
109
|
/** Whether the chat button shows a notification badge */
|
|
105
110
|
chatBadge?: boolean;
|
|
106
111
|
children: ReactNode;
|
|
112
|
+
/** Test ID for testing frameworks */
|
|
113
|
+
testID?: string;
|
|
107
114
|
}
|
|
108
115
|
declare const Sidebar: React.FC<SidebarProps>;
|
|
109
116
|
|
|
@@ -123,6 +130,8 @@ interface SidebarGroupProps {
|
|
|
123
130
|
/** Docks this group to the bottom of the expanded sidebar (e.g. Settings, Billing). */
|
|
124
131
|
pinnedToBottom?: boolean;
|
|
125
132
|
children: ReactNode;
|
|
133
|
+
/** Test ID for testing frameworks */
|
|
134
|
+
testID?: string;
|
|
126
135
|
}
|
|
127
136
|
declare const SidebarGroup: React.FC<SidebarGroupProps>;
|
|
128
137
|
|
|
@@ -153,6 +162,8 @@ interface SidebarMenuItemProps {
|
|
|
153
162
|
extra?: ReactNode;
|
|
154
163
|
/** Whether this item is inside a SidebarMenuSub (parent collapsible sets nested styling) */
|
|
155
164
|
isNested?: boolean;
|
|
165
|
+
/** Test ID for testing frameworks */
|
|
166
|
+
testID?: string;
|
|
156
167
|
}
|
|
157
168
|
declare const SidebarMenuItem: React.FC<SidebarMenuItemProps>;
|
|
158
169
|
|
|
@@ -163,6 +174,8 @@ interface SidebarMenuCollapsibleProps {
|
|
|
163
174
|
/** Route prefixes that should auto-expand this collapsible */
|
|
164
175
|
matchPaths?: string[];
|
|
165
176
|
children: ReactNode;
|
|
177
|
+
/** Test ID for testing frameworks */
|
|
178
|
+
testID?: string;
|
|
166
179
|
}
|
|
167
180
|
declare const SidebarMenuCollapsible: React.FC<SidebarMenuCollapsibleProps>;
|
|
168
181
|
|
|
@@ -178,6 +191,8 @@ declare const SidebarMenuSub: React.FC<{
|
|
|
178
191
|
interface SidebarChatButtonProps {
|
|
179
192
|
onClick?: () => void;
|
|
180
193
|
badge?: boolean;
|
|
194
|
+
/** Test ID for testing frameworks */
|
|
195
|
+
testID?: string;
|
|
181
196
|
}
|
|
182
197
|
declare const SidebarChatButton: React.FC<SidebarChatButtonProps>;
|
|
183
198
|
|
|
@@ -189,6 +204,8 @@ interface SidebarCollapsedProps {
|
|
|
189
204
|
onChatClick?: () => void;
|
|
190
205
|
showChat?: boolean;
|
|
191
206
|
chatBadge?: boolean;
|
|
207
|
+
/** Test ID for testing frameworks */
|
|
208
|
+
testID?: string;
|
|
192
209
|
}
|
|
193
210
|
declare const SidebarCollapsed: React.FC<SidebarCollapsedProps>;
|
|
194
211
|
|
|
@@ -210,6 +227,8 @@ interface SidebarPinnedListProps {
|
|
|
210
227
|
onReorder?: (fromKey: string, toKey: string) => void;
|
|
211
228
|
/** Render 8px top/bottom spacers when items are present. Default true. */
|
|
212
229
|
spacers?: boolean;
|
|
230
|
+
/** Test ID for testing frameworks */
|
|
231
|
+
testID?: string;
|
|
213
232
|
}
|
|
214
233
|
/**
|
|
215
234
|
* Renders the pinned shortcuts list with pointer-event-based drag-and-drop
|
package/native/index.d.ts
CHANGED
|
@@ -16,7 +16,10 @@ interface SidebarLinkProps {
|
|
|
16
16
|
isActive?: SidebarLinkActiveCheck;
|
|
17
17
|
onClick?: SidebarLinkClickHandler;
|
|
18
18
|
dataId?: string;
|
|
19
|
+
"aria-label"?: string;
|
|
19
20
|
children: ReactNode;
|
|
21
|
+
/** Test ID for testing frameworks */
|
|
22
|
+
testID?: string;
|
|
20
23
|
}
|
|
21
24
|
interface SidebarContextValue {
|
|
22
25
|
collapsed: boolean;
|
|
@@ -32,6 +35,8 @@ interface SidebarProviderProps {
|
|
|
32
35
|
pathname?: string;
|
|
33
36
|
linkComponent?: React.ComponentType<SidebarLinkProps>;
|
|
34
37
|
children: ReactNode;
|
|
38
|
+
/** Test ID for testing frameworks */
|
|
39
|
+
testID?: string;
|
|
35
40
|
}
|
|
36
41
|
declare const SidebarProvider: React.FC<SidebarProviderProps>;
|
|
37
42
|
declare const useSidebar: () => SidebarContextValue;
|
|
@@ -104,6 +109,8 @@ interface SidebarProps {
|
|
|
104
109
|
/** Whether the chat button shows a notification badge */
|
|
105
110
|
chatBadge?: boolean;
|
|
106
111
|
children: ReactNode;
|
|
112
|
+
/** Test ID for testing frameworks */
|
|
113
|
+
testID?: string;
|
|
107
114
|
}
|
|
108
115
|
declare const Sidebar: React.FC<SidebarProps>;
|
|
109
116
|
|
|
@@ -123,6 +130,8 @@ interface SidebarGroupProps {
|
|
|
123
130
|
/** Docks this group to the bottom of the expanded sidebar (e.g. Settings, Billing). */
|
|
124
131
|
pinnedToBottom?: boolean;
|
|
125
132
|
children: ReactNode;
|
|
133
|
+
/** Test ID for testing frameworks */
|
|
134
|
+
testID?: string;
|
|
126
135
|
}
|
|
127
136
|
declare const SidebarGroup: React.FC<SidebarGroupProps>;
|
|
128
137
|
|
|
@@ -153,6 +162,8 @@ interface SidebarMenuItemProps {
|
|
|
153
162
|
extra?: ReactNode;
|
|
154
163
|
/** Whether this item is inside a SidebarMenuSub (parent collapsible sets nested styling) */
|
|
155
164
|
isNested?: boolean;
|
|
165
|
+
/** Test ID for testing frameworks */
|
|
166
|
+
testID?: string;
|
|
156
167
|
}
|
|
157
168
|
declare const SidebarMenuItem: React.FC<SidebarMenuItemProps>;
|
|
158
169
|
|
|
@@ -163,6 +174,8 @@ interface SidebarMenuCollapsibleProps {
|
|
|
163
174
|
/** Route prefixes that should auto-expand this collapsible */
|
|
164
175
|
matchPaths?: string[];
|
|
165
176
|
children: ReactNode;
|
|
177
|
+
/** Test ID for testing frameworks */
|
|
178
|
+
testID?: string;
|
|
166
179
|
}
|
|
167
180
|
declare const SidebarMenuCollapsible: React.FC<SidebarMenuCollapsibleProps>;
|
|
168
181
|
|
|
@@ -178,6 +191,8 @@ declare const SidebarMenuSub: React.FC<{
|
|
|
178
191
|
interface SidebarChatButtonProps {
|
|
179
192
|
onClick?: () => void;
|
|
180
193
|
badge?: boolean;
|
|
194
|
+
/** Test ID for testing frameworks */
|
|
195
|
+
testID?: string;
|
|
181
196
|
}
|
|
182
197
|
declare const SidebarChatButton: React.FC<SidebarChatButtonProps>;
|
|
183
198
|
|
|
@@ -189,6 +204,8 @@ interface SidebarCollapsedProps {
|
|
|
189
204
|
onChatClick?: () => void;
|
|
190
205
|
showChat?: boolean;
|
|
191
206
|
chatBadge?: boolean;
|
|
207
|
+
/** Test ID for testing frameworks */
|
|
208
|
+
testID?: string;
|
|
192
209
|
}
|
|
193
210
|
declare const SidebarCollapsed: React.FC<SidebarCollapsedProps>;
|
|
194
211
|
|
|
@@ -210,6 +227,8 @@ interface SidebarPinnedListProps {
|
|
|
210
227
|
onReorder?: (fromKey: string, toKey: string) => void;
|
|
211
228
|
/** Render 8px top/bottom spacers when items are present. Default true. */
|
|
212
229
|
spacers?: boolean;
|
|
230
|
+
/** Test ID for testing frameworks */
|
|
231
|
+
testID?: string;
|
|
213
232
|
}
|
|
214
233
|
/**
|
|
215
234
|
* Renders the pinned shortcuts list with pointer-event-based drag-and-drop
|
package/native/index.js
CHANGED
|
@@ -62,7 +62,9 @@ var DefaultLink = ({
|
|
|
62
62
|
className,
|
|
63
63
|
children,
|
|
64
64
|
dataId,
|
|
65
|
-
|
|
65
|
+
testID,
|
|
66
|
+
onClick,
|
|
67
|
+
"aria-label": ariaLabel
|
|
66
68
|
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
67
69
|
"a",
|
|
68
70
|
{
|
|
@@ -71,6 +73,8 @@ var DefaultLink = ({
|
|
|
71
73
|
target: external ? target ?? "_blank" : target ?? void 0,
|
|
72
74
|
rel: external ? "noopener noreferrer" : void 0,
|
|
73
75
|
"data-id": dataId,
|
|
76
|
+
"data-testid": testID,
|
|
77
|
+
"aria-label": ariaLabel,
|
|
74
78
|
onClick,
|
|
75
79
|
children
|
|
76
80
|
}
|
|
@@ -420,6 +424,62 @@ var SingleLinkWrap = import_styled_components.default.div`
|
|
|
420
424
|
text-decoration: none;
|
|
421
425
|
}
|
|
422
426
|
`;
|
|
427
|
+
var CollapsedIconLinkWrap = import_styled_components.default.div`
|
|
428
|
+
width: ${(p) => p.$size}px;
|
|
429
|
+
height: ${(p) => p.$size}px;
|
|
430
|
+
flex-shrink: 0;
|
|
431
|
+
|
|
432
|
+
& > a,
|
|
433
|
+
& > * {
|
|
434
|
+
display: flex;
|
|
435
|
+
width: 100%;
|
|
436
|
+
height: 100%;
|
|
437
|
+
align-items: center;
|
|
438
|
+
justify-content: center;
|
|
439
|
+
border: 0;
|
|
440
|
+
border-radius: ${(p) => p.$radius}px;
|
|
441
|
+
background: ${(p) => p.$active ? p.$activeBg : "transparent"};
|
|
442
|
+
color: ${(p) => p.$active ? p.$hoverColor : p.$color};
|
|
443
|
+
cursor: pointer;
|
|
444
|
+
padding: 0;
|
|
445
|
+
font: inherit;
|
|
446
|
+
text-decoration: none;
|
|
447
|
+
transition:
|
|
448
|
+
background-color 0.15s ease-in-out,
|
|
449
|
+
color 0.15s ease-in-out;
|
|
450
|
+
|
|
451
|
+
${(p) => p.$active && `
|
|
452
|
+
outline: 1px solid ${p.$activeOutline};
|
|
453
|
+
outline-offset: -1px;
|
|
454
|
+
`}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
& > a:hover,
|
|
458
|
+
& > *:hover {
|
|
459
|
+
background-color: ${(p) => p.$hoverBg};
|
|
460
|
+
color: ${(p) => p.$hoverColor};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
& > a:focus-visible,
|
|
464
|
+
& > *:focus-visible {
|
|
465
|
+
outline: ${(p) => p.$focusOutlineWidth}px solid ${(p) => p.$focusOutline};
|
|
466
|
+
outline-offset: -${(p) => p.$focusOutlineWidth}px;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
& > a > span,
|
|
470
|
+
& > * > span {
|
|
471
|
+
display: flex;
|
|
472
|
+
width: ${(p) => p.$iconSize}px;
|
|
473
|
+
height: ${(p) => p.$iconSize}px;
|
|
474
|
+
align-items: center;
|
|
475
|
+
justify-content: center;
|
|
476
|
+
|
|
477
|
+
svg {
|
|
478
|
+
width: ${(p) => p.$iconSize}px;
|
|
479
|
+
height: ${(p) => p.$iconSize}px;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
`;
|
|
423
483
|
var isChildActive = (item, pathname) => {
|
|
424
484
|
if (!item.children || !pathname) {
|
|
425
485
|
return false;
|
|
@@ -434,6 +494,7 @@ var isChildActive = (item, pathname) => {
|
|
|
434
494
|
};
|
|
435
495
|
var CollapsedIconItem = ({ item, onPopoverOpen, onPopoverClose, pathname, sizing, colors }) => {
|
|
436
496
|
const ref = (0, import_react2.useRef)(null);
|
|
497
|
+
const { LinkComponent } = useSidebar();
|
|
437
498
|
const hasChildren = Boolean(item.children && item.children.length > 0);
|
|
438
499
|
const isActive = hasChildren && isChildActive(item, pathname);
|
|
439
500
|
const openPopover = (0, import_react2.useCallback)(() => {
|
|
@@ -459,6 +520,42 @@ var CollapsedIconItem = ({ item, onPopoverOpen, onPopoverClose, pathname, sizing
|
|
|
459
520
|
if (!item.icon) {
|
|
460
521
|
return null;
|
|
461
522
|
}
|
|
523
|
+
if (!hasChildren) {
|
|
524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
525
|
+
CollapsedIconLinkWrap,
|
|
526
|
+
{
|
|
527
|
+
ref,
|
|
528
|
+
onMouseEnter: openPopover,
|
|
529
|
+
onMouseLeave: onPopoverClose,
|
|
530
|
+
onFocus: openPopover,
|
|
531
|
+
onBlur: onPopoverClose,
|
|
532
|
+
"data-id": item.dataId,
|
|
533
|
+
$size: sizing.itemHeight,
|
|
534
|
+
$radius: sizing.radius,
|
|
535
|
+
$iconSize: sizing.iconSize,
|
|
536
|
+
$color: colors.content.tertiary,
|
|
537
|
+
$hoverBg: colors.overlay.mono,
|
|
538
|
+
$hoverColor: colors.content.primary,
|
|
539
|
+
$activeBg: colors.overlay.mono,
|
|
540
|
+
$activeOutline: colors.border.secondary,
|
|
541
|
+
$focusOutline: colors.border.brand,
|
|
542
|
+
$focusOutlineWidth: sizing.focusOutlineWidth,
|
|
543
|
+
$active: isSimpleActive,
|
|
544
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
545
|
+
LinkComponent,
|
|
546
|
+
{
|
|
547
|
+
to: item.to,
|
|
548
|
+
exact: item.exact,
|
|
549
|
+
external: item.external,
|
|
550
|
+
target: item.target,
|
|
551
|
+
dataId: item.dataId,
|
|
552
|
+
"aria-label": typeof item.label === "string" ? item.label : void 0,
|
|
553
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: item.icon })
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
}
|
|
557
|
+
);
|
|
558
|
+
}
|
|
462
559
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
463
560
|
IconBtn,
|
|
464
561
|
{
|
|
@@ -470,7 +567,7 @@ var CollapsedIconItem = ({ item, onPopoverOpen, onPopoverClose, pathname, sizing
|
|
|
470
567
|
onBlur: onPopoverClose,
|
|
471
568
|
onKeyDown: handleKeyDown,
|
|
472
569
|
"data-id": item.dataId,
|
|
473
|
-
"aria-haspopup":
|
|
570
|
+
"aria-haspopup": "menu",
|
|
474
571
|
$size: sizing.itemHeight,
|
|
475
572
|
$radius: sizing.radius,
|
|
476
573
|
$iconSize: sizing.iconSize,
|