Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NavList add defaultOpen to NavList.Item #3698

Merged
merged 5 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fifty-seas-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@primer/react': patch
---

Adds the defaultOpen prop to NavList.Item

<!-- Changed components: _none_ -->
5 changes: 5 additions & 0 deletions src/NavList/NavList.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
"defaultValue": "false",
"description": "Set `aria-current` to `\"page\"` to indicate that the item represents the current page. Set `aria-current` to `\"location\"` to indicate that the item represents the current location on a page. For more information about `aria-current`, see [MDN](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)."
},
{
"name": "defaultOpen",
"type": "boolean",
"description": "The open state of the item when it is initially rendered if the item has a SubNav."
},
{
"name": "ref",
"type": "React.RefObject<HTMLAnchorElement>"
Expand Down
7 changes: 6 additions & 1 deletion src/NavList/NavList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export const WithNestedSubItems: Story = () => (
<PageLayout>
<PageLayout.Pane position="start">
<NavList>
<NavList.Item href="#">Item 1</NavList.Item>
<NavList.Item defaultOpen={true} href="#">
Item 1
<NavList.SubNav>
<NavList.Item href="#">Sub item 1</NavList.Item>
</NavList.SubNav>
</NavList.Item>
<NavList.Item href="#">
Item 2{/* NOTE: Don't nest SubNavs. For testing purposes only */}
<NavList.SubNav>
Expand Down
16 changes: 11 additions & 5 deletions src/NavList/NavList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ Root.displayName = 'NavList'

export type NavListItemProps = {
children: React.ReactNode
defaultOpen?: boolean
href?: string
'aria-current'?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false' | boolean
} & SxProp

const Item = React.forwardRef<HTMLAnchorElement, NavListItemProps>(
({'aria-current': ariaCurrent, children, sx: sxProp = defaultSxProp, ...props}, ref) => {
({'aria-current': ariaCurrent, children, defaultOpen, sx: sxProp = defaultSxProp, ...props}, ref) => {
const {depth} = React.useContext(SubNavContext)

// Get SubNav from children
Expand All @@ -64,10 +65,14 @@ const Item = React.forwardRef<HTMLAnchorElement, NavListItemProps>(
isValidElement(child) ? child.type !== SubNav : true,
)

if (!isValidElement(subNav) && defaultOpen)
// eslint-disable-next-line no-console
console.error('NavList.Item must have a NavList.SubNav to use defaultOpen.')

// Render ItemWithSubNav if SubNav is present
if (subNav && isValidElement(subNav)) {
return (
<ItemWithSubNav subNav={subNav} depth={depth} sx={sxProp}>
<ItemWithSubNav subNav={subNav} depth={depth} defaultOpen={defaultOpen} sx={sxProp}>
{childrenWithoutSubNav}
</ItemWithSubNav>
)
Expand Down Expand Up @@ -96,6 +101,7 @@ type ItemWithSubNavProps = {
children: React.ReactNode
subNav: React.ReactNode
depth: number
defaultOpen?: boolean
} & SxProp

const ItemWithSubNavContext = React.createContext<{buttonId: string; subNavId: string; isOpen: boolean}>({
Expand All @@ -106,10 +112,10 @@ const ItemWithSubNavContext = React.createContext<{buttonId: string; subNavId: s

// TODO: ref prop
// TODO: Animate open/close transition
function ItemWithSubNav({children, subNav, depth, sx: sxProp = defaultSxProp}: ItemWithSubNavProps) {
function ItemWithSubNav({children, subNav, depth, defaultOpen, sx: sxProp = defaultSxProp}: ItemWithSubNavProps) {
const buttonId = useId()
const subNavId = useId()
const [isOpen, setIsOpen] = React.useState(false)
const [isOpen, setIsOpen] = React.useState((defaultOpen || null) ?? false)
const subNavRef = React.useRef<HTMLDivElement>(null)
const [containsCurrentItem, setContainsCurrentItem] = React.useState(false)

Expand All @@ -124,7 +130,7 @@ function ItemWithSubNav({children, subNav, depth, sx: sxProp = defaultSxProp}: I
setIsOpen(true)
}
}
}, [subNav])
}, [subNav, buttonId])

return (
<ItemWithSubNavContext.Provider value={{buttonId, subNavId, isOpen}}>
Expand Down
Loading