Skip to content

Commit 30f93ff

Browse files
authored
NavList: Add sx prop (#2077)
* Add support for `sx` prop to NavList components * Default sx to empty object * Create happy-brooms-swim.md * Update snapshot
1 parent c8f7e23 commit 30f93ff

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

.changeset/happy-brooms-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
Adds support for the `sx` prop on the draft implementation of `NavList` and all its subcomponents (e.g., `NavList.Item`)

src/NavList/NavList.tsx

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import {ChevronDownIcon} from '@primer/octicons-react'
22
import {useSSRSafeId} from '@react-aria/ssr'
33
import React, {isValidElement} from 'react'
4+
import styled from 'styled-components'
45
import {ActionList} from '../ActionList'
56
import Box from '../Box'
67
import StyledOcticon from '../StyledOcticon'
8+
import sx, {merge, SxProp} from '../sx'
79

810
// ----------------------------------------------------------------------------
911
// NavList
1012

1113
export type NavListProps = {
1214
children: React.ReactNode
13-
} & React.ComponentProps<'nav'>
15+
} & SxProp &
16+
React.ComponentProps<'nav'>
17+
18+
const NavBox = styled.nav<SxProp>(sx)
1419

15-
// TODO: sx prop
1620
const Root = React.forwardRef<HTMLElement, NavListProps>(({children, ...props}, ref) => {
1721
return (
18-
<nav ref={ref} {...props}>
22+
<NavBox {...props} ref={ref}>
1923
<ActionList>{children}</ActionList>
20-
</nav>
24+
</NavBox>
2125
)
2226
})
2327

@@ -30,12 +34,11 @@ export type NavListItemProps = {
3034
children: React.ReactNode
3135
href?: string
3236
'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false' | boolean
33-
}
37+
} & SxProp
3438

35-
// TODO: sx prop
3639
// TODO: as prop
3740
const Item = React.forwardRef<HTMLAnchorElement, NavListItemProps>(
38-
({href, 'aria-current': ariaCurrent, children}, ref) => {
41+
({href, 'aria-current': ariaCurrent, children, sx: sxProp = {}}, ref) => {
3942
const {depth} = React.useContext(SubNavContext)
4043

4144
// Get SubNav from children
@@ -54,7 +57,7 @@ const Item = React.forwardRef<HTMLAnchorElement, NavListItemProps>(
5457
)
5558

5659
return (
57-
<ItemWithSubNav subNav={subNav} subNavContainsCurrentItem={Boolean(currentItem)}>
60+
<ItemWithSubNav subNav={subNav} subNavContainsCurrentItem={Boolean(currentItem)} sx={sxProp}>
5861
{childrenWithoutSubNav}
5962
</ItemWithSubNav>
6063
)
@@ -66,11 +69,14 @@ const Item = React.forwardRef<HTMLAnchorElement, NavListItemProps>(
6669
href={href}
6770
aria-current={ariaCurrent}
6871
active={Boolean(ariaCurrent) && ariaCurrent !== 'false'}
69-
sx={{
70-
paddingLeft: depth > 0 ? 5 : null, // Indent sub-items
71-
fontSize: depth > 0 ? 0 : null, // Reduce font size of sub-items
72-
fontWeight: depth > 0 ? 'normal' : null // Sub-items don't get bolded
73-
}}
72+
sx={merge<SxProp['sx']>(
73+
{
74+
paddingLeft: depth > 0 ? 5 : null, // Indent sub-items
75+
fontSize: depth > 0 ? 0 : null, // Reduce font size of sub-items
76+
fontWeight: depth > 0 ? 'normal' : null // Sub-items don't get bolded
77+
},
78+
sxProp
79+
)}
7480
>
7581
{children}
7682
</ActionList.LinkItem>
@@ -87,17 +93,16 @@ type ItemWithSubNavProps = {
8793
children: React.ReactNode
8894
subNav: React.ReactNode
8995
subNavContainsCurrentItem: boolean
90-
}
96+
} & SxProp
9197

9298
const ItemWithSubNavContext = React.createContext<{buttonId: string; subNavId: string}>({
9399
buttonId: '',
94100
subNavId: ''
95101
})
96102

97-
// TODO: sx prop
98103
// TODO: ref prop
99104
// TODO: Animate open/close transition
100-
function ItemWithSubNav({children, subNav, subNavContainsCurrentItem}: ItemWithSubNavProps) {
105+
function ItemWithSubNav({children, subNav, subNavContainsCurrentItem, sx: sxProp = {}}: ItemWithSubNavProps) {
101106
const buttonId = useSSRSafeId()
102107
const subNavId = useSSRSafeId()
103108
// SubNav starts open if current item is in it
@@ -114,9 +119,12 @@ function ItemWithSubNav({children, subNav, subNavContainsCurrentItem}: ItemWithS
114119
// When the subNav is closed, how should we indicated that the subNav contains the current item?
115120
active={!isOpen && subNavContainsCurrentItem}
116121
onClick={() => setIsOpen(open => !open)}
117-
sx={{
118-
fontWeight: subNavContainsCurrentItem ? 'bold' : null // Parent item is bold if any of it's sub-items are current
119-
}}
122+
sx={merge<SxProp['sx']>(
123+
{
124+
fontWeight: subNavContainsCurrentItem ? 'bold' : null // Parent item is bold if any of it's sub-items are current
125+
},
126+
sxProp
127+
)}
120128
>
121129
{children}
122130
{/* What happens if the user provides a TrailingVisual? */}
@@ -141,14 +149,13 @@ function ItemWithSubNav({children, subNav, subNavContainsCurrentItem}: ItemWithS
141149

142150
type NavListSubNavProps = {
143151
children: React.ReactNode
144-
}
152+
} & SxProp
145153

146154
const SubNavContext = React.createContext<{depth: number}>({depth: 0})
147155

148-
// TODO: sx prop
149156
// TODO: ref prop
150157
// NOTE: SubNav must be a direct child of an Item
151-
const SubNav = ({children}: NavListSubNavProps) => {
158+
const SubNav = ({children, sx: sxProp = {}}: NavListSubNavProps) => {
152159
const {buttonId, subNavId} = React.useContext(ItemWithSubNavContext)
153160
const {depth} = React.useContext(SubNavContext)
154161

@@ -165,7 +172,18 @@ const SubNav = ({children}: NavListSubNavProps) => {
165172

166173
return (
167174
<SubNavContext.Provider value={{depth: depth + 1}}>
168-
<Box as="ul" id={subNavId} aria-labelledby={buttonId} sx={{padding: 0, margin: 0}}>
175+
<Box
176+
as="ul"
177+
id={subNavId}
178+
aria-labelledby={buttonId}
179+
sx={merge<SxProp['sx']>(
180+
{
181+
padding: 0,
182+
margin: 0
183+
},
184+
sxProp
185+
)}
186+
>
169187
{children}
170188
</Box>
171189
</SubNavContext.Provider>
@@ -198,19 +216,20 @@ Divider.displayName = 'NavList.Divider'
198216
// ----------------------------------------------------------------------------
199217
// NavList.Group
200218

201-
type NavListGroupProps = React.PropsWithChildren<{
219+
type NavListGroupProps = {
202220
children: React.ReactNode
203221
title?: string
204-
}>
222+
} & SxProp
205223

206-
// TODO: sx prop
207224
// TODO: ref prop
208-
const Group = ({title, children}: NavListGroupProps) => {
225+
const Group = ({title, children, sx: sxProp = {}}: NavListGroupProps) => {
209226
return (
210227
<>
211228
{/* Hide divider if the group is the first item in the list */}
212229
<ActionList.Divider sx={{'&:first-child': {display: 'none'}}} />
213-
<ActionList.Group title={title}>{children}</ActionList.Group>
230+
<ActionList.Group title={title} sx={sxProp}>
231+
{children}
232+
</ActionList.Group>
214233
</>
215234
)
216235
}

src/NavList/__snapshots__/NavList.test.tsx.snap

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,9 @@ exports[`NavList renders a simple list 1`] = `
294294
}
295295
296296
<div>
297-
<nav>
297+
<nav
298+
class=""
299+
>
298300
<ul
299301
class="c0"
300302
>
@@ -695,7 +697,9 @@ exports[`NavList renders with groups 1`] = `
695697
}
696698
697699
<div>
698-
<nav>
700+
<nav
701+
class=""
702+
>
699703
<ul
700704
class="c0"
701705
>
@@ -1172,7 +1176,9 @@ exports[`NavList.Item with NavList.SubNav does not have active styles if SubNav
11721176
}
11731177
11741178
<div>
1175-
<nav>
1179+
<nav
1180+
class=""
1181+
>
11761182
<ul
11771183
class="c0"
11781184
>
@@ -1480,7 +1486,9 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t
14801486
}
14811487
14821488
<div>
1483-
<nav>
1489+
<nav
1490+
class=""
1491+
>
14841492
<ul
14851493
class="c0"
14861494
>

0 commit comments

Comments
 (0)