Skip to content
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