-
Notifications
You must be signed in to change notification settings - Fork 645
TreeView: Implement markup #2305
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
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
91203b8
Add TreeView docs
colebemis d425776
Update TreeView props
colebemis 6523985
Scaffold treeview markup
colebemis 30bbae4
Add TreeView to drafts
colebemis 15dc5b2
Add comment
colebemis 46fd1d5
Track item levels
colebemis a980f76
Add TreeView stories
colebemis 9961985
Update TreeView docs
colebemis 945c58c
Update TreeView markup
colebemis 5b03f71
Create curly-birds-argue.md
colebemis 4072099
Merge branch 'main' into treeview-markup
colebemis 109f05e
Fix examples
colebemis 49303b0
Update src/TreeView/TreeView.tsx
colebemis cbebf14
Update docs/content/TreeView.mdx
colebemis b3a3096
Add some tests
colebemis 3f3ab76
Merge branch 'main' into treeview-markup
colebemis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@primer/react": patch | ||
| --- | ||
|
|
||
| Add draft TreeView component |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| --- | ||
| title: TreeView | ||
| componentId: tree_view | ||
| status: Draft | ||
| description: A hierarchical list of items where nested items can be expanded and collapsed. | ||
| --- | ||
|
|
||
| ## Examples | ||
|
|
||
| ### File tree navigation without directory links | ||
|
|
||
| ```jsx live drafts | ||
| <nav aria-label="File navigation"> | ||
| <TreeView aria-label="File navigation"> | ||
| <TreeView.Item> | ||
| src | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Avatar.tsx</TreeView.LinkItem> | ||
| <TreeView.Item> | ||
| Button | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Button.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">Button.test.tsx</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| <TreeView.Item> | ||
| public | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">index.html</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">favicon.ico</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| <TreeView.LinkItem href="#">package.json</TreeView.LinkItem> | ||
| </TreeView> | ||
| </nav> | ||
| ``` | ||
|
|
||
| ### File tree navigation with directory links | ||
|
|
||
| ```jsx live drafts | ||
| <nav aria-label="File navigation"> | ||
| <TreeView aria-label="File navigation"> | ||
| <TreeView.LinkItem href="#"> | ||
| src | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Avatar.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#"> | ||
| Button | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Button.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">Button.test.tsx</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#"> | ||
| public | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">index.html</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">favicon.ico</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">package.json</TreeView.LinkItem> | ||
| </TreeView> | ||
| </nav> | ||
| ``` | ||
|
|
||
| ## Props | ||
|
|
||
| ### TreeView | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow name="children" type="React.ReactNode" required /> | ||
| {/* <PropsTableSxRow /> */} | ||
| </PropsTable> | ||
|
|
||
| ### TreeView.Item | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow name="children" type="React.ReactNode" required /> | ||
| <PropsTableRow | ||
| name="onSelect" | ||
| type="(event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void" | ||
| /> | ||
| <PropsTableRow name="onToggle" type="(isExpanded: boolean) => void" /> | ||
| {/* <PropsTableSxRow /> */} | ||
| </PropsTable> | ||
|
|
||
| ### TreeView.LinkItem | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow name="children" type="React.ReactNode" required /> | ||
| <PropsTableRow | ||
| name="href" | ||
| type="string" | ||
| description={ | ||
| <> | ||
| The URL that the item navigates to. <InlineCode>href</InlineCode> is passed to the underlying{' '} | ||
| <InlineCode><a></InlineCode> element. If <InlineCode>as</InlineCode> is specified, the component may need | ||
| different props. If the item contains a sub-nav, the item is rendered as a{' '} | ||
| <InlineCode><button></InlineCode> and <InlineCode>href</InlineCode> is ignored. | ||
| </> | ||
| } | ||
| /> | ||
| <PropsTableRow | ||
| name="onSelect" | ||
| type="(event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void" | ||
| /> | ||
| <PropsTableRow name="onToggle" type="(isExpanded: boolean) => void" /> | ||
| {/* <PropsTableSxRow /> */} | ||
| </PropsTable> | ||
|
|
||
| ### TreeView.SubTree | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow name="children" type="React.ReactNode" /> | ||
| {/* <PropsTableSxRow /> */} | ||
| </PropsTable> | ||
|
|
||
| <!-- TODO: Add leading and trailing visuals --> | ||
|
|
||
| <!-- ### TreeView.LeadingVisual | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow | ||
| name="children" | ||
| type={`| React.ReactNode | ||
| | (props: {isExpanded: boolean}) => React.ReactNode`} | ||
| /> | ||
| <PropsTableSxRow /> | ||
| </PropsTable> | ||
|
|
||
| ### TreeView.TrailingVisual | ||
|
|
||
| <PropsTable> | ||
| <PropsTableRow | ||
| name="children" | ||
| type={`| React.ReactNode | ||
| | (props: {isExpanded: boolean}) => React.ReactNode`} | ||
| /> | ||
| <PropsTableSxRow /> | ||
| </PropsTable> | ||
|
|
||
| ### TreeView.FolderIcon | ||
|
|
||
| <PropsTable> | ||
| <PropsTableSxRow /> | ||
| </PropsTable> --> | ||
|
|
||
| <!-- TODO: Add components to support async behavior (e.g. LoadingItem) --> | ||
|
|
||
| ## Status | ||
|
|
||
| <ComponentChecklist | ||
| items={{ | ||
| propsDocumented: true, | ||
| noUnnecessaryDeps: false, | ||
| adaptsToThemes: false, | ||
| adaptsToScreenSizes: false, | ||
| fullTestCoverage: false, | ||
| usedInProduction: false, | ||
| usageExamplesDocumented: false, | ||
| hasStorybookStories: false, | ||
| designReviewed: false, | ||
| a11yReviewed: false, | ||
| stableApi: false, | ||
| addressedApiFeedback: false, | ||
| hasDesignGuidelines: false, | ||
| hasFigmaComponent: false | ||
| }} | ||
| /> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import {Meta, Story} from '@storybook/react' | ||
| import {TreeView} from './TreeView' | ||
| import React from 'react' | ||
| import Box from '../Box' | ||
|
|
||
| const meta: Meta = { | ||
| title: 'Composite components/TreeView', | ||
| component: TreeView, | ||
| parameters: { | ||
| layout: 'fullscreen' | ||
| } | ||
| } | ||
|
|
||
| export const FileTreeWithDirectoryLinks: Story = () => ( | ||
| <Box p={3}> | ||
| <nav aria-label="File navigation"> | ||
| <TreeView aria-label="File navigation"> | ||
| <TreeView.LinkItem href="#"> | ||
| src | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Avatar.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#"> | ||
| Button | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Button.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">Button.test.tsx</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| <TreeView.LinkItem | ||
| href="#" | ||
| // eslint-disable-next-line no-console | ||
| onToggle={isExpanded => console.log(`${isExpanded ? 'Expanded' : 'Collapsed'} "public" folder `)} | ||
| > | ||
| public | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">index.html</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">favicon.ico</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">package.json</TreeView.LinkItem> | ||
| </TreeView> | ||
| </nav> | ||
| </Box> | ||
| ) | ||
|
|
||
| export const FileTreeWithoutDirectoryLinks: Story = () => ( | ||
| <Box p={3}> | ||
| <nav aria-label="File navigation"> | ||
| <TreeView aria-label="File navigation"> | ||
| <TreeView.Item> | ||
| src | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Avatar.tsx</TreeView.LinkItem> | ||
| <TreeView.Item> | ||
| Button | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">Button.tsx</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">Button.test.tsx</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| <TreeView.Item | ||
| // eslint-disable-next-line no-console | ||
| onToggle={isExpanded => console.log(`${isExpanded ? 'Expanded' : 'Collapsed'} "public" folder `)} | ||
| > | ||
| public | ||
| <TreeView.SubTree> | ||
| <TreeView.LinkItem href="#">index.html</TreeView.LinkItem> | ||
| <TreeView.LinkItem href="#">favicon.ico</TreeView.LinkItem> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| <TreeView.LinkItem href="#">package.json</TreeView.LinkItem> | ||
| </TreeView> | ||
| </nav> | ||
| </Box> | ||
| ) | ||
|
|
||
| export default meta |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import {render} from '@testing-library/react' | ||
| import React from 'react' | ||
| import {TreeView} from './TreeView' | ||
|
|
||
| it('uses tree role', () => { | ||
| const {queryByRole} = render( | ||
| <TreeView aria-label="Test tree"> | ||
| <TreeView.Item>Item 1</TreeView.Item> | ||
| <TreeView.Item>Item 2</TreeView.Item> | ||
| <TreeView.Item>Item 3</TreeView.Item> | ||
| </TreeView> | ||
| ) | ||
|
|
||
| const root = queryByRole('tree') | ||
|
|
||
| expect(root).toHaveAccessibleName('Test tree') | ||
| }) | ||
|
|
||
| it('uses treeitem role', () => { | ||
| const {queryAllByRole} = render( | ||
| <TreeView aria-label="Test tree"> | ||
| <TreeView.Item>Item 1</TreeView.Item> | ||
| <TreeView.Item>Item 2</TreeView.Item> | ||
| <TreeView.Item>Item 3</TreeView.Item> | ||
| </TreeView> | ||
| ) | ||
|
|
||
| const items = queryAllByRole('treeitem') | ||
|
|
||
| expect(items).toHaveLength(3) | ||
| }) | ||
|
|
||
| it('hides subtrees by default', () => { | ||
| const {queryByRole} = render( | ||
| <TreeView aria-label="Test tree"> | ||
| <TreeView.Item> | ||
| Parent | ||
| <TreeView.SubTree> | ||
| <TreeView.Item>Child</TreeView.Item> | ||
| </TreeView.SubTree> | ||
| </TreeView.Item> | ||
| </TreeView> | ||
| ) | ||
|
|
||
| const parentItem = queryByRole('treeitem', {name: 'Parent'}) | ||
| const subtree = queryByRole('group') | ||
|
|
||
| expect(parentItem).toHaveAttribute('aria-expanded', 'false') | ||
| expect(subtree).toBeNull() | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.