@pharos-ds/core 1.0.10-canary.2189f39 → 1.0.10
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/SideNav/SideNav.d.ts +1 -5
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +2 -8
- package/dist/pharos.umd.js +12 -12
- package/dist/pharos.umd.js.map +3 -3
- package/package.json +1 -1
- package/src/SideNav/SideNav.doc.mjs +4 -13
- package/src/SideNav/SideNav.test.tsx +0 -65
- package/src/SideNav/SideNav.tsx +3 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pharos-ds/core",
|
|
3
|
-
"version": "1.0.10
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"displayName": "Pharos Core",
|
|
5
5
|
"description": "Pharos component library. Accessible, themeable React components with spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Caio Silvestre",
|
|
@@ -25,7 +25,7 @@ export const docs = {
|
|
|
25
25
|
{className: 'pharos-side-nav-section', states: ['collapsed']},
|
|
26
26
|
],
|
|
27
27
|
},
|
|
28
|
-
description: 'Container with five zones: header, topContent, children (scrollable), footer, and footerIcons. Supports collapsible mode
|
|
28
|
+
description: 'Container with five zones: header, topContent, children (scrollable), footer, and footerIcons. Supports collapsible mode.',
|
|
29
29
|
props: [
|
|
30
30
|
{
|
|
31
31
|
name: 'header',
|
|
@@ -71,7 +71,7 @@ export const docs = {
|
|
|
71
71
|
{
|
|
72
72
|
name: 'footer',
|
|
73
73
|
type: 'ReactNode',
|
|
74
|
-
description: 'Footer area above the icon bar.
|
|
74
|
+
description: 'Footer area above the icon bar.',
|
|
75
75
|
slotElements: [
|
|
76
76
|
{
|
|
77
77
|
__element: 'Text',
|
|
@@ -85,7 +85,7 @@ export const docs = {
|
|
|
85
85
|
{
|
|
86
86
|
name: 'footerIcons',
|
|
87
87
|
type: 'ReactNode',
|
|
88
|
-
description: 'Footer icon bar.
|
|
88
|
+
description: 'Footer icon bar.',
|
|
89
89
|
slotElements: [
|
|
90
90
|
{
|
|
91
91
|
__element: 'Icon',
|
|
@@ -99,7 +99,7 @@ export const docs = {
|
|
|
99
99
|
{
|
|
100
100
|
name: 'collapsible',
|
|
101
101
|
type: 'boolean | { defaultIsCollapsed?: boolean; isCollapsed?: boolean; onCollapsedChange?: (isCollapsed: boolean) => void; hasButton?: boolean; buttonLabel?: string }',
|
|
102
|
-
description: 'Enables collapse behavior. true for uncontrolled with default toggle button, or an object for controlled mode and advanced config (defaultIsCollapsed, isCollapsed + onCollapsedChange, hasButton, buttonLabel).
|
|
102
|
+
description: 'Enables collapse behavior. true for uncontrolled with default toggle button, or an object for controlled mode and advanced config (defaultIsCollapsed, isCollapsed + onCollapsedChange, hasButton, buttonLabel).',
|
|
103
103
|
default: 'false',
|
|
104
104
|
},
|
|
105
105
|
{
|
|
@@ -131,9 +131,6 @@ export const docs = {
|
|
|
131
131
|
bestPractices: [
|
|
132
132
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
133
133
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
134
|
-
{guidance: true, description: 'Make the SideNav of an app shell collapsible so users can fold it into an icon rail; control it with collapsible={{isCollapsed, onCollapsedChange}} to persist the choice.'},
|
|
135
|
-
{guidance: true, description: 'Give every top-level SideNavItem an icon: the collapsed rail hides items without one. Swap the header for a compact mark (e.g. a symbol logo) by reading useSideNavCollapse().isCollapsed.'},
|
|
136
|
-
{guidance: true, description: 'Put actions that must stay reachable in the rail (settings, account, theme, sign out) in footerIcons as icon buttons; footer is hidden while collapsed, so use it only for full-width extras such as promo cards or density controls.'},
|
|
137
134
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
138
135
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
139
136
|
],
|
|
@@ -153,9 +150,6 @@ export const docsZh = {
|
|
|
153
150
|
bestPractices: [
|
|
154
151
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
155
152
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
156
|
-
{guidance: true, description: 'Make the SideNav of an app shell collapsible so users can fold it into an icon rail; control it with collapsible={{isCollapsed, onCollapsedChange}} to persist the choice.'},
|
|
157
|
-
{guidance: true, description: 'Give every top-level SideNavItem an icon: the collapsed rail hides items without one. Swap the header for a compact mark (e.g. a symbol logo) by reading useSideNavCollapse().isCollapsed.'},
|
|
158
|
-
{guidance: true, description: 'Put actions that must stay reachable in the rail (settings, account, theme, sign out) in footerIcons as icon buttons; footer is hidden while collapsed, so use it only for full-width extras such as promo cards or density controls.'},
|
|
159
153
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
160
154
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
161
155
|
],
|
|
@@ -175,9 +169,6 @@ export const docsDense = {
|
|
|
175
169
|
bestPractices: [
|
|
176
170
|
{guidance: true, description: 'Use sections to group related navigation items and help users scan for their destination.'},
|
|
177
171
|
{guidance: true, description: 'Pair outline and filled icon variants so the selected state is visually distinct.'},
|
|
178
|
-
{guidance: true, description: 'Make the SideNav of an app shell collapsible so users can fold it into an icon rail; control it with collapsible={{isCollapsed, onCollapsedChange}} to persist the choice.'},
|
|
179
|
-
{guidance: true, description: 'Give every top-level SideNavItem an icon: the collapsed rail hides items without one. Swap the header for a compact mark (e.g. a symbol logo) by reading useSideNavCollapse().isCollapsed.'},
|
|
180
|
-
{guidance: true, description: 'Put actions that must stay reachable in the rail (settings, account, theme, sign out) in footerIcons as icon buttons; footer is hidden while collapsed, so use it only for full-width extras such as promo cards or density controls.'},
|
|
181
172
|
{guidance: false, description: 'Include a SideNavHeading when a TopNav is already providing app identity; this duplicates branding.'},
|
|
182
173
|
{guidance: false, description: 'Use for filtering content; use tabs or filter buttons instead.'},
|
|
183
174
|
],
|
|
@@ -183,71 +183,6 @@ describe('SideNav', () => {
|
|
|
183
183
|
expect(nav.children).toHaveLength(1);
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
-
describe('footer in the collapsed rail', () => {
|
|
187
|
-
it('hides footer but keeps footerIcons and the collapse button when collapsed', () => {
|
|
188
|
-
render(
|
|
189
|
-
<SideNav
|
|
190
|
-
collapsible={{defaultIsCollapsed: true}}
|
|
191
|
-
footer={<span data-testid="footer">Footer</span>}
|
|
192
|
-
footerIcons={<span data-testid="icons">Icons</span>}>
|
|
193
|
-
<SideNavItem label="Dashboard" icon={StubIcon} />
|
|
194
|
-
</SideNav>,
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
expect(screen.queryByTestId('footer')).not.toBeInTheDocument();
|
|
198
|
-
expect(screen.getByTestId('icons')).toBeInTheDocument();
|
|
199
|
-
expect(
|
|
200
|
-
screen.getByRole('button', {name: 'Expand sidebar'}),
|
|
201
|
-
).toBeInTheDocument();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('shows footer again after expanding the rail', async () => {
|
|
205
|
-
const user = userEvent.setup();
|
|
206
|
-
render(
|
|
207
|
-
<SideNav
|
|
208
|
-
collapsible={{defaultIsCollapsed: true}}
|
|
209
|
-
footer={<span data-testid="footer">Footer</span>}>
|
|
210
|
-
<SideNavItem label="Dashboard" icon={StubIcon} />
|
|
211
|
-
</SideNav>,
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
expect(screen.queryByTestId('footer')).not.toBeInTheDocument();
|
|
215
|
-
await user.click(screen.getByRole('button', {name: 'Expand sidebar'}));
|
|
216
|
-
expect(screen.getByTestId('footer')).toBeInTheDocument();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('does not leave an empty footer container when only footer is set and the rail has no button', () => {
|
|
220
|
-
render(
|
|
221
|
-
<SideNav
|
|
222
|
-
data-testid="nav"
|
|
223
|
-
collapsible={{isCollapsed: true, hasButton: false}}
|
|
224
|
-
footer={<span data-testid="footer">Footer</span>}>
|
|
225
|
-
Content
|
|
226
|
-
</SideNav>,
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
expect(screen.queryByTestId('footer')).not.toBeInTheDocument();
|
|
230
|
-
expect(screen.getByTestId('nav').children).toHaveLength(1);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('keeps rendering footer when the SideNav is expanded or not collapsible', () => {
|
|
234
|
-
const {unmount} = render(
|
|
235
|
-
<SideNav collapsible footer={<span data-testid="footer">Footer</span>}>
|
|
236
|
-
Content
|
|
237
|
-
</SideNav>,
|
|
238
|
-
);
|
|
239
|
-
expect(screen.getByTestId('footer')).toBeInTheDocument();
|
|
240
|
-
unmount();
|
|
241
|
-
|
|
242
|
-
render(
|
|
243
|
-
<SideNav footer={<span data-testid="footer">Footer</span>}>
|
|
244
|
-
Content
|
|
245
|
-
</SideNav>,
|
|
246
|
-
);
|
|
247
|
-
expect(screen.getByTestId('footer')).toBeInTheDocument();
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
186
|
it('fires a consumer onClick on the collapse button in addition to toggling', async () => {
|
|
252
187
|
const user = userEvent.setup();
|
|
253
188
|
const onClick = vi.fn();
|
package/src/SideNav/SideNav.tsx
CHANGED
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Sidebar navigation container with five zones: header + topContent (sticky together),
|
|
12
12
|
* children (scrollable), footer, and footerIcons (sticky bottom).
|
|
13
|
-
* When collapsed to the icon rail, `footer` is hidden; `footerIcons` and the
|
|
14
|
-
* collapse button remain.
|
|
15
13
|
*
|
|
16
14
|
* Supports optional resize via drag handle at the inline-end edge.
|
|
17
15
|
*
|
|
@@ -214,9 +212,7 @@ export interface SideNavProps extends BaseProps<HTMLElement> {
|
|
|
214
212
|
*/
|
|
215
213
|
children: ReactNode;
|
|
216
214
|
/**
|
|
217
|
-
* Footer area above icon bar (e.g., promo cards).
|
|
218
|
-
* sidebar is collapsed to the icon rail (full-width content doesn't fit);
|
|
219
|
-
* use `footerIcons` for actions that must stay visible in the rail.
|
|
215
|
+
* Footer area above icon bar (e.g., promo cards).
|
|
220
216
|
*/
|
|
221
217
|
footer?: ReactNode;
|
|
222
218
|
/**
|
|
@@ -488,11 +484,7 @@ export function SideNav({
|
|
|
488
484
|
// Default mode — full sidebar
|
|
489
485
|
// =========================================================================
|
|
490
486
|
const hasStickyTop = !!(header || topContent);
|
|
491
|
-
|
|
492
|
-
// full-width (promo cards, segmented controls, account blocks) and doesn't
|
|
493
|
-
// fit the icon-only rail. `footerIcons` and the collapse button stay.
|
|
494
|
-
const visibleFooter = collapsed ? null : footer;
|
|
495
|
-
const hasStickyBottom = !!(visibleFooter || footerIcons);
|
|
487
|
+
const hasStickyBottom = !!(footer || footerIcons);
|
|
496
488
|
// The built-in collapse button only renders when collapse is enabled and it
|
|
497
489
|
// hasn't been opted out via `collapsible.hasButton: false` (e.g. when the
|
|
498
490
|
// consumer places a SideNavCollapseButton in the header instead).
|
|
@@ -545,7 +537,7 @@ export function SideNav({
|
|
|
545
537
|
styles.stickyBottom,
|
|
546
538
|
collapsed && styles.stickyBottomCollapsed,
|
|
547
539
|
)}>
|
|
548
|
-
{
|
|
540
|
+
{footer}
|
|
549
541
|
<div
|
|
550
542
|
{...stylex.props(
|
|
551
543
|
styles.footerRow,
|