Skip to content

Use SSR-compatible slot implementation in TreeView #3097

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

Merged
merged 5 commits into from
Mar 31, 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/sharp-eggs-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@primer/react": patch
---

`TreeView` is now SSR-compatible.

Warning: In this new implementation, `TreeView.LeadingVisual` and `TreeView.TrailingView` must be direct children of `TreeView.Item`.
2 changes: 1 addition & 1 deletion generated/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -5121,4 +5121,4 @@
"subcomponents": []
}
}
}
}
35 changes: 14 additions & 21 deletions src/TreeView/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import {
import classnames from 'classnames'
import React from 'react'
import styled, {keyframes} from 'styled-components'
import {get} from '../constants'
import {ConfirmationDialog} from '../Dialog/ConfirmationDialog'
import Spinner from '../Spinner'
import Text from '../Text'
import VisuallyHidden from '../_VisuallyHidden'
import {get} from '../constants'
import {useControllableState} from '../hooks/useControllableState'
import {useId} from '../hooks/useId'
import useSafeTimeout from '../hooks/useSafeTimeout'
import Spinner from '../Spinner'
import {useSlots} from '../hooks/useSlots'
import sx, {SxProp} from '../sx'
import Text from '../Text'
import createSlots from '../utils/create-slots'
import VisuallyHidden from '../_VisuallyHidden'
import {getAccessibleName} from './shared'
import {getFirstChildElement, useRovingTabIndex} from './useRovingTabIndex'
import {useTypeahead} from './useTypeahead'
Expand Down Expand Up @@ -302,8 +302,6 @@ export type TreeViewItemProps = {
onSelect?: (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void
}

const {Slots, Slot} = createSlots(['LeadingVisual', 'TrailingVisual'])

const Item = React.forwardRef<HTMLElement, TreeViewItemProps>(
(
{
Expand All @@ -318,6 +316,7 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>(
},
ref,
) => {
const [slots, rest] = useSlots(children, {leadingVisual: LeadingVisual, trailingVisual: TrailingVisual})
const {expandedStateCache} = React.useContext(RootContext)
const labelId = useId()
const leadingVisualId = useId()
Expand All @@ -333,7 +332,7 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>(
onChange: onExpandedChange,
})
const {level} = React.useContext(ItemContext)
const {hasSubTree, subTree, childrenWithoutSubTree} = useSubTree(children)
const {hasSubTree, subTree, childrenWithoutSubTree} = useSubTree(rest)
const [isSubTreeEmpty, setIsSubTreeEmpty] = React.useState(!hasSubTree)
const [isFocused, setIsFocused] = React.useState(false)

Expand Down Expand Up @@ -462,15 +461,9 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>(
</div>
) : null}
<div id={labelId} className="PRIVATE_TreeView-item-content">
<Slots>
{slots => (
<>
{slots.LeadingVisual}
<span className="PRIVATE_TreeView-item-content-text">{childrenWithoutSubTree}</span>
{slots.TrailingVisual}
</>
)}
</Slots>
{slots.leadingVisual}
<span className="PRIVATE_TreeView-item-content-text">{childrenWithoutSubTree}</span>
{slots.trailingVisual}
</div>
</div>
{subTree}
Expand Down Expand Up @@ -757,14 +750,14 @@ const LeadingVisual: React.FC<TreeViewVisualProps> = props => {
const {isExpanded, leadingVisualId} = React.useContext(ItemContext)
const children = typeof props.children === 'function' ? props.children({isExpanded}) : props.children
return (
<Slot name="LeadingVisual">
<>
<div className="PRIVATE_VisuallyHidden" aria-hidden={true} id={leadingVisualId}>
{props.label}
</div>
<div className="PRIVATE_TreeView-item-visual" aria-hidden={true}>
{children}
</div>
</Slot>
</>
)
}

Expand All @@ -774,14 +767,14 @@ const TrailingVisual: React.FC<TreeViewVisualProps> = props => {
const {isExpanded, trailingVisualId} = React.useContext(ItemContext)
const children = typeof props.children === 'function' ? props.children({isExpanded}) : props.children
return (
<Slot name="TrailingVisual">
<>
<div className="PRIVATE_VisuallyHidden" aria-hidden={true} id={trailingVisualId}>
{props.label}
</div>
<div className="PRIVATE_TreeView-item-visual" aria-hidden={true}>
{children}
</div>
</Slot>
</>
)
}

Expand Down
3 changes: 2 additions & 1 deletion src/hooks/useSlots.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react'
import {warning} from '../utils/warning'

export type SlotConfig = Record<string, React.ComponentType>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type SlotConfig = Record<string, React.ComponentType<any>>

type SlotElements<Type extends SlotConfig> = {
[Property in keyof Type]: React.ReactElement
Expand Down