Skip to content

Commit

Permalink
TreeView: Align tree item toggle and visual icons to top of item (#…
Browse files Browse the repository at this point in the history
…4572)

* Pin tree item toggle icon to top

* Create rich-pans-press.md

* Fix horizontal centering

* center instead of sace-around

* Add story

* Top align visual icons in multiline treeview items

* Update patch message

* Fix comment

* Revert return

* test(vrt): update snapshots

* Remove unneccesary align

* test(vrt): update snapshots

---------

Co-authored-by: Stephanie Hong <41085564+JelloBagel@users.noreply.github.com>
Co-authored-by: JelloBagel <JelloBagel@users.noreply.github.com>
  • Loading branch information
3 people committed May 20, 2024
1 parent c5f7940 commit ac25029
Show file tree
Hide file tree
Showing 48 changed files with 111 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/rich-pans-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

TreeView: Always align expand/collapse chevron icon, leading visual, and trailing visual to top of item
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions packages/react/src/TreeView/TreeView.features.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1040,4 +1040,88 @@ export const LeadingAction: Story = () => {
)
}

export const MultilineItems: Story = () => (
<nav aria-label="Files changed">
<TreeView aria-label="Files changed">
<TreeView.Item id="src" defaultExpanded>
<TreeView.LeadingVisual>
<TreeView.DirectoryIcon />
</TreeView.LeadingVisual>
<div style={{whiteSpace: 'wrap'}}>
this is a very long directory name that we have intentionally allowed to wrap over multiple lines to
demonstrate alignment
</div>
<TreeView.SubTree>
<TreeView.Item id="src/Avatar.tsx">
<TreeView.LeadingVisual>
<FileIcon />
</TreeView.LeadingVisual>
Avatar.tsx
<TreeView.TrailingVisual>
<Octicon icon={DiffAddedIcon} color="success.fg" aria-label="Added" />
</TreeView.TrailingVisual>
</TreeView.Item>
</TreeView.SubTree>
</TreeView.Item>
<TreeView.Item id="src" defaultExpanded>
<TreeView.LeadingVisual>
<TreeView.DirectoryIcon />
</TreeView.LeadingVisual>
<div style={{whiteSpace: 'wrap'}}>
this is a medium directory name that we wrap over 2 lines to demonstrate alignment
</div>
<TreeView.TrailingVisual>
<Octicon icon={DiffAddedIcon} color="success.fg" aria-label="Added" />
</TreeView.TrailingVisual>
<TreeView.SubTree>
<TreeView.Item id="src/Avatar.tsx">
<TreeView.LeadingVisual>
<FileIcon />
</TreeView.LeadingVisual>
Avatar.tsx
<TreeView.TrailingVisual>
<Octicon icon={DiffAddedIcon} color="success.fg" aria-label="Added" />
</TreeView.TrailingVisual>
</TreeView.Item>
</TreeView.SubTree>
</TreeView.Item>
<TreeView.Item id="src" defaultExpanded>
<TreeView.LeadingVisual>
<TreeView.DirectoryIcon />
</TreeView.LeadingVisual>
this is a very long directory name that we have intentionally NOT allowed to wrap over multiple lines to
demonstrate alignment
<TreeView.SubTree>
<TreeView.Item id="src/Avatar.tsx">
<TreeView.LeadingVisual>
<FileIcon />
</TreeView.LeadingVisual>
Avatar.tsx
<TreeView.TrailingVisual>
<Octicon icon={DiffAddedIcon} color="success.fg" aria-label="Added" />
</TreeView.TrailingVisual>
</TreeView.Item>
</TreeView.SubTree>
</TreeView.Item>
<TreeView.Item id="src" defaultExpanded>
<TreeView.LeadingVisual>
<TreeView.DirectoryIcon />
</TreeView.LeadingVisual>
short name
<TreeView.SubTree>
<TreeView.Item id="src/Avatar.tsx">
<TreeView.LeadingVisual>
<FileIcon />
</TreeView.LeadingVisual>
Avatar.tsx
<TreeView.TrailingVisual>
<Octicon icon={DiffAddedIcon} color="success.fg" aria-label="Added" />
</TreeView.TrailingVisual>
</TreeView.Item>
</TreeView.SubTree>
</TreeView.Item>
</TreeView>
</nav>
)

export default meta
27 changes: 22 additions & 5 deletions packages/react/src/TreeView/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export type TreeViewProps = {
className?: string
}

/* Size of toggle icon in pixels. */
const TOGGLE_ICON_SIZE = 12

const UlBox = styled.ul<SxProp>`
list-style: none;
padding: 0;
Expand Down Expand Up @@ -105,14 +108,14 @@ const UlBox = styled.ul<SxProp>`
.PRIVATE_TreeView-item-container {
--level: 1; /* default level */
--toggle-width: 1rem; /* 16px */
--min-item-height: 2rem; /* 32px */
position: relative;
display: grid;
--leading-action-width: calc(var(--has-leading-action, 0) * 1.5rem);
--spacer-width: calc(calc(var(--level) - 1) * (var(--toggle-width) / 2));
grid-template-columns: var(--spacer-width) var(--leading-action-width) var(--toggle-width) 1fr;
grid-template-areas: 'spacer leadingAction toggle content';
width: 100%;
min-height: 2rem; /* 32px */
font-size: ${get('fontSizes.1')};
color: ${get('colors.fg.default')};
border-radius: ${get('radii.2')};
Expand All @@ -129,7 +132,7 @@ const UlBox = styled.ul<SxProp>`
@media (pointer: coarse) {
--toggle-width: 1.5rem; /* 24px */
min-height: 2.75rem; /* 44px */
--min-item-height: 2.75rem; /* 44px */
}
&:has(.PRIVATE_TreeView-item-skeleton):hover {
Expand Down Expand Up @@ -169,8 +172,11 @@ const UlBox = styled.ul<SxProp>`
.PRIVATE_TreeView-item-toggle {
grid-area: toggle;
display: flex;
align-items: center;
justify-content: center;
align-items: flex-start;
/* The toggle should appear vertically centered for single-line items, but remain at the top for items that wrap
across more lines. */
padding-top: calc(var(--min-item-height) / 2 - ${TOGGLE_ICON_SIZE}px / 2);
height: 100%;
color: ${get('colors.fg.muted')};
}
Expand All @@ -187,10 +193,13 @@ const UlBox = styled.ul<SxProp>`
.PRIVATE_TreeView-item-content {
grid-area: content;
display: flex;
align-items: center;
height: 100%;
padding: 0 ${get('space.2')};
gap: ${get('space.2')};
line-height: var(--custom-line-height, var(--text-body-lineHeight-medium, 1.4285));
/* The dynamic top and bottom padding to maintain the minimum item height for single line items */
padding-top: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);
padding-bottom: calc((var(--min-item-height) - var(--custom-line-height, 1.3rem)) / 2);
}
.PRIVATE_TreeView-item-content-text {
Expand All @@ -204,7 +213,11 @@ const UlBox = styled.ul<SxProp>`
.PRIVATE_TreeView-item-visual {
display: flex;
align-items: center;
color: ${get('colors.fg.muted')};
/* The visual icons should appear vertically centered for single-line items, but remain at the top for items that wrap
across more lines. */
height: var(--custom-line-height, 1.3rem);
}
.PRIVATE_TreeView-item-leading-action {
Expand Down Expand Up @@ -524,7 +537,11 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>(
}
}}
>
{isExpanded ? <ChevronDownIcon size={12} /> : <ChevronRightIcon size={12} />}
{isExpanded ? (
<ChevronDownIcon size={TOGGLE_ICON_SIZE} />
) : (
<ChevronRightIcon size={TOGGLE_ICON_SIZE} />
)}
</div>
) : null}
<div id={labelId} className="PRIVATE_TreeView-item-content">
Expand Down

0 comments on commit ac25029

Please sign in to comment.