@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pharos-ds/core",
3
- "version": "1.0.10-canary.2189f39",
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: the collapsed rail shows only item icons, footerIcons, and the collapse button (footer is hidden).',
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. Hidden while the SideNav is collapsed to the icon rail, since full-width content does not fit; put actions that must stay reachable in footerIcons.',
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. Stays visible in the collapsed rail, next to the collapse button.',
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). When collapsed, the rail shows item icons only (items without icons are hidden), keeps footerIcons and the collapse button, and does not render footer. Read the state inside the nav with useSideNavCollapse().',
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();
@@ -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). Not rendered while the
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
- // In the collapsed rail, `footer` is not rendered: its content is laid out
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
- {visibleFooter}
540
+ {footer}
549
541
  <div
550
542
  {...stylex.props(
551
543
  styles.footerRow,