Skip to content

Commit 182bec9

Browse files
authored
Merge branch 'main' into treeview-error-dialog
2 parents b41d85b + c367b44 commit 182bec9

File tree

4 files changed

+294
-74
lines changed

4 files changed

+294
-74
lines changed

.changeset/tender-turtles-serve.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+
TreeView: Add support for a skeleton state with the TreeView.SubTree `count` prop

docs/content/TreeView.mdx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ description: A hierarchical list of items where nested items can be expanded and
1111

1212
```jsx live drafts
1313
<Box sx={{maxWidth: 400}}>
14-
<nav aria-label="File navigation">
15-
<TreeView aria-label="File navigation">
14+
<nav aria-label="Files">
15+
<TreeView aria-label="Files">
1616
<TreeView.Item>
1717
<TreeView.LeadingVisual>
1818
<TreeView.DirectoryIcon />
@@ -57,8 +57,8 @@ description: A hierarchical list of items where nested items can be expanded and
5757

5858
```jsx live drafts
5959
<Box sx={{maxWidth: 400}}>
60-
<nav aria-label="File navigation">
61-
<TreeView aria-label="File navigation">
60+
<nav aria-label="Files">
61+
<TreeView aria-label="Files">
6262
<TreeView.LinkItem href="#">
6363
<TreeView.LeadingVisual>
6464
<TreeView.DirectoryIcon />
@@ -113,8 +113,8 @@ function ControlledTreeView() {
113113
return (
114114
<Box sx={{display: 'grid', gap: 2, maxWidth: 400}}>
115115
<Button onClick={() => setExpanded(!expanded)}>{expanded ? 'Collapse' : 'Expand'}</Button>
116-
<nav aria-label="File navigation">
117-
<TreeView aria-label="File navigation">
116+
<nav aria-label="Files">
117+
<TreeView aria-label="Files">
118118
<TreeView.Item expanded={expanded} onExpandedChange={setExpanded}>
119119
src
120120
<TreeView.SubTree>
@@ -139,8 +139,8 @@ To render stateful visuals, pass a render function to `TreeView.LeadingVisual` o
139139

140140
```jsx live drafts
141141
<Box sx={{maxWidth: 400}}>
142-
<nav aria-label="File navigation">
143-
<TreeView aria-label="File navigation">
142+
<nav aria-label="Files">
143+
<TreeView aria-label="Files">
144144
<TreeView.Item>
145145
<TreeView.LeadingVisual>
146146
{({isExpanded}) => (isExpanded ? <FileDirectoryOpenFillIcon /> : <FileDirectoryFillIcon />)}
@@ -162,8 +162,8 @@ Since stateful directory icons are a common use case for TreeView, we provide a
162162

163163
```jsx live drafts
164164
<Box sx={{maxWidth: 400}}>
165-
<nav aria-label="File navigation">
166-
<TreeView aria-label="File navigation">
165+
<nav aria-label="Files">
166+
<TreeView aria-label="Files">
167167
<TreeView.Item>
168168
<TreeView.LeadingVisual>
169169
<TreeView.DirectoryIcon />
@@ -343,7 +343,12 @@ See [Storybook](https://primer.style/react/storybook?path=/story/components-tree
343343
</>
344344
}
345345
/>
346-
{/* <PropsTableSxRow /> */}
346+
<PropsTableRow
347+
name="count"
348+
type="number"
349+
description="The number of items expected to be in the subtree. When in the loading state, the subtree will render a skeleton loading placeholder with the specified count of items"
350+
/>
351+
<PropsTableSxRow />
347352
</PropsTable>
348353

349354
### TreeView.ErrorDialog

src/TreeView/TreeView.stories.tsx

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ const meta: Meta = {
1818

1919
export const FileTreeWithDirectoryLinks: Story = () => (
2020
<Box sx={{p: 3, maxWidth: 400}}>
21-
<nav aria-label="File navigation">
22-
<TreeView aria-label="File navigation">
21+
<nav aria-label="Files">
22+
<TreeView aria-label="Files">
2323
<TreeView.LinkItem href="#src">
2424
<TreeView.LeadingVisual>
2525
<TreeView.DirectoryIcon />
@@ -98,8 +98,8 @@ export const FileTreeWithDirectoryLinks: Story = () => (
9898
export const FileTreeWithoutDirectoryLinks: Story = () => {
9999
return (
100100
<Box sx={{p: 3, maxWidth: 400}}>
101-
<nav aria-label="File navigation">
102-
<TreeView aria-label="File navigation">
101+
<nav aria-label="Files">
102+
<TreeView aria-label="Files">
103103
<TreeView.Item defaultExpanded>
104104
<TreeView.LeadingVisual>
105105
<TreeView.DirectoryIcon />
@@ -343,9 +343,9 @@ export const Controlled: Story = () => {
343343
</ActionMenu.Overlay>
344344
</ActionMenu>
345345
</Box>
346-
<nav aria-label="File navigation">
346+
<nav aria-label="Files">
347347
<CurrentPathContext.Provider value={{currentPath, setCurrentPath}}>
348-
<TreeView aria-label="File navigation">
348+
<TreeView aria-label="Files">
349349
{tree.map(item => (
350350
<TreeItem
351351
key={item.data.name}
@@ -418,8 +418,8 @@ export const AsyncSuccess: Story = args => {
418418

419419
return (
420420
<Box sx={{p: 3}}>
421-
<nav aria-label="File navigation">
422-
<TreeView aria-label="File navigation">
421+
<nav aria-label="Files">
422+
<TreeView aria-label="Files">
423423
<TreeView.Item>
424424
<TreeView.LeadingVisual>
425425
<FileIcon />
@@ -470,6 +470,107 @@ AsyncSuccess.args = {
470470
responseTime: 2000
471471
}
472472

473+
export const AsyncWithCount: Story = args => {
474+
const [isLoading, setIsLoading] = React.useState(false)
475+
const [asyncItems, setAsyncItems] = React.useState<string[]>([])
476+
477+
let state: SubTreeState = 'initial'
478+
479+
if (isLoading) {
480+
state = 'loading'
481+
} else if (asyncItems.length > 0) {
482+
state = 'done'
483+
}
484+
485+
return (
486+
<Box sx={{p: 3}}>
487+
<nav aria-label="Files">
488+
<TreeView aria-label="Files">
489+
<TreeView.Item
490+
onExpandedChange={async isExpanded => {
491+
if (asyncItems.length === 0 && isExpanded) {
492+
setIsLoading(true)
493+
494+
// Load items
495+
const items = await loadItems(args.responseTime)
496+
497+
setIsLoading(false)
498+
setAsyncItems(items)
499+
}
500+
}}
501+
>
502+
<TreeView.LeadingVisual>
503+
<TreeView.DirectoryIcon />
504+
</TreeView.LeadingVisual>
505+
Directory with async items
506+
<TreeView.SubTree state={state} count={args.count}>
507+
{asyncItems.map(item => (
508+
<TreeView.Item key={item}>
509+
<TreeView.LeadingVisual>
510+
<FileIcon />
511+
</TreeView.LeadingVisual>
512+
{item}
513+
</TreeView.Item>
514+
))}
515+
</TreeView.SubTree>
516+
</TreeView.Item>
517+
<TreeView.LinkItem href="#src">
518+
<TreeView.LeadingVisual>
519+
<TreeView.DirectoryIcon />
520+
</TreeView.LeadingVisual>
521+
src
522+
<TreeView.SubTree>
523+
<TreeView.LinkItem href="#avatar-tsx">
524+
<TreeView.LeadingVisual>
525+
<FileIcon />
526+
</TreeView.LeadingVisual>
527+
Avatar.tsx
528+
</TreeView.LinkItem>
529+
<TreeView.LinkItem href="#button" current>
530+
<TreeView.LeadingVisual>
531+
<TreeView.DirectoryIcon />
532+
</TreeView.LeadingVisual>
533+
Button
534+
<TreeView.SubTree>
535+
<TreeView.LinkItem href="#button-tsx">
536+
<TreeView.LeadingVisual>
537+
<FileIcon />
538+
</TreeView.LeadingVisual>
539+
Button.tsx
540+
</TreeView.LinkItem>
541+
<TreeView.LinkItem href="#button-test-tsx">
542+
<TreeView.LeadingVisual>
543+
<FileIcon />
544+
</TreeView.LeadingVisual>
545+
Button.test.tsx
546+
</TreeView.LinkItem>
547+
</TreeView.SubTree>
548+
</TreeView.LinkItem>
549+
<TreeView.Item>
550+
<TreeView.LeadingVisual>
551+
<FileIcon />
552+
</TreeView.LeadingVisual>
553+
ReallyLongFileNameThatShouldBeTruncated.tsx
554+
</TreeView.Item>
555+
</TreeView.SubTree>
556+
</TreeView.LinkItem>
557+
</TreeView>
558+
</nav>
559+
</Box>
560+
)
561+
}
562+
563+
AsyncWithCount.args = {
564+
responseTime: 2000,
565+
count: 3
566+
}
567+
568+
AsyncWithCount.argTypes = {
569+
count: {
570+
type: 'number'
571+
}
572+
}
573+
473574
async function alwaysFails(responseTime: number) {
474575
await wait(responseTime)
475576
throw new Error('Failed to load items')
@@ -509,8 +610,8 @@ export const AsyncError: Story = args => {
509610

510611
return (
511612
<Box sx={{p: 3}}>
512-
<nav aria-label="File navigation">
513-
<TreeView aria-label="File navigation">
613+
<nav aria-label="Files">
614+
<TreeView aria-label="Files">
514615
<TreeView.Item>
515616
<TreeView.LeadingVisual>
516617
<FileIcon />

0 commit comments

Comments
 (0)