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

Add truncation features to the LabelGroup component #3264

Merged
merged 46 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5bcd432
adds props to LabelGroup to support truncation
mperrotti May 1, 2023
4338301
adds tests and gets IntersectionObserver re-rendering with correct bt…
mperrotti May 2, 2023
0d9f6ea
fixes bug where expand button width was not being accounted for in th…
mperrotti May 3, 2023
2a53152
writes tests, fixes bugs, updates stories to use Label children
mperrotti May 4, 2023
6f618e1
fixes type errors and removes code copied from primer/behaviors
mperrotti May 4, 2023
e10de4b
code cleanup
mperrotti May 4, 2023
7eb79fd
Merge branch 'main' into mp/labelgroup-truncation
mperrotti May 8, 2023
51d3b7e
Update generated/components.json
mperrotti May 8, 2023
05f1a4f
Create blue-dragons-marry.md
mperrotti May 8, 2023
fa6d686
fixes typos
mperrotti May 9, 2023
fb35767
Merge branch 'mp/labelgroup-truncation' of github.com:primer/react in…
mperrotti May 9, 2023
012d4e2
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti May 9, 2023
f5ec9a2
tests focus mgmt when expanding inline interactive tokens
mperrotti May 9, 2023
9c04f80
fixes lint errors and overlay sizing issues
mperrotti May 10, 2023
d62b112
Merge branch 'main' into mp/labelgroup-truncation
mperrotti May 30, 2023
b0d57d4
Merge branch 'main' into mp/labelgroup-truncation
mperrotti May 30, 2023
6c56373
Merge branch 'main' into mp/labelgroup-truncation
mperrotti May 30, 2023
4c0b108
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jun 5, 2023
d76b018
updates inline collapse button to use text, not an icon
mperrotti Jun 5, 2023
e70d5e9
test(vrt): update snapshots
mperrotti Jun 5, 2023
67873a8
rm defaultProps
mperrotti Jun 6, 2023
046a7fb
Merge branch 'mp/labelgroup-truncation' of github.com:primer/react in…
mperrotti Jun 6, 2023
b2a730d
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jun 6, 2023
83173bd
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 7, 2023
9d48bd3
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 7, 2023
14c7467
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 7, 2023
9799398
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 15, 2023
d2336a3
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 16, 2023
d50b56d
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jun 27, 2023
d8d1ba6
disables focusZone in the overlay
mperrotti Jul 12, 2023
6780fa1
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jul 12, 2023
44f4917
updates changeset
mperrotti Jul 12, 2023
614cfdb
updates changeset again
mperrotti Jul 12, 2023
aaecaad
adds count to visually hidden content
mperrotti Jul 13, 2023
3e665c6
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jul 13, 2023
ebd150c
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jul 14, 2023
150549a
layout refinements
mperrotti Jul 14, 2023
bf2bd18
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 14, 2023
9cf6b50
Merge branch 'main' of github.com:primer/react into mp/labelgroup-tru…
mperrotti Jul 19, 2023
a93200c
Merge branch 'mp/labelgroup-truncation' of github.com:primer/react in…
mperrotti Jul 19, 2023
2565f7e
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 20, 2023
b0fe2da
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 21, 2023
e4180ef
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 21, 2023
729d0df
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 25, 2023
923f605
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 26, 2023
674274b
Merge branch 'main' into mp/labelgroup-truncation
mperrotti Jul 28, 2023
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
11 changes: 11 additions & 0 deletions .changeset/blue-dragons-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@primer/react": patch
---

Adds truncation features to the LabelGroup component:
- truncate LabelGroup children after a static number of children (for example, truncate after the 5th label)
- truncate LabelGroup children to fit in the width of the parent
- show full list in an Overlay
- show full list inline

<!-- Changed components: LabelGroup -->
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.
70 changes: 70 additions & 0 deletions e2e/components/LabelGroup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,74 @@ test.describe('LabelGroup', () => {
})
}
})

test.describe('Truncated to 5', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-labelgroup--playground',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`LabelGroup.TruncateToFive.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-labelgroup-features--truncate-after-five',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})

test.describe('Truncated to fit', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-labelgroup--playground',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`LabelGroup.TruncateAuto.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-labelgroup-features--truncate-auto',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})

test.describe('LabelGroup Interactions', () => {
test('inline overflow - focuses the first focusable token that was previously hidden', async ({page}) => {
await visit(page, {
id: 'components-labelgroup-features--truncate-auto-expand-inline-with-interactive-tokens',
globals: {
colorScheme: 'light',
},
})
await page.locator('button', {hasText: '+5'}).click()

await expect(page.locator('button', {hasText: 'Twelve'})).toBeFocused()
})
})
})
12 changes: 12 additions & 0 deletions src/AnchoredOverlay/AnchoredOverlay.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@
"defaultValue": "'start'",
"description": ""
},
{
"name": "alignmentOffset",
"type": "number",
"defaultValue": "4",
"description": "An additional offset, in pixels, to move the floating element from the aligning edge. Positive values move the floating element in the direction of center-alignment. Negative values move the floating element away from center-alignment. When align is `'center`, positive offsets move the floating element right (top or bottom anchor side) or down (left or right anchor side). If using outside positioning, or if `align` is set to `'center'`, this defaults to `0` instead of `4`."
},
{
"name": "anchorOffset",
"type": "number",
"defaultValue": "4",
"description": "The number of pixels between the anchor edge and the floating element. Positive values move the floating element farther from the anchor element (for outside positioning) or further inside the anchor element (for inside positioning). Negative values have the opposite effect. If `side` is set to `'inside-center'`, this defaults to `0` instead of `4`."
},
{
"name": "overlayProps",
"type": "Partial<OverlayProps>",
Expand Down
6 changes: 5 additions & 1 deletion src/AnchoredOverlay/AnchoredOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ interface AnchoredOverlayBaseProps extends Pick<OverlayProps, 'height' | 'width'

export type AnchoredOverlayProps = AnchoredOverlayBaseProps &
(AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor) &
Partial<Pick<PositionSettings, 'align' | 'side'>>
Partial<Pick<PositionSettings, 'align' | 'side' | 'anchorOffset' | 'alignmentOffset'>>

/**
* An `AnchoredOverlay` provides an anchor that will open a floating overlay positioned relative to the anchor.
Expand All @@ -101,6 +101,8 @@ export const AnchoredOverlay: React.FC<React.PropsWithChildren<AnchoredOverlayPr
focusZoneSettings,
side = 'outside-bottom',
align = 'start',
alignmentOffset,
broccolinisoup marked this conversation as resolved.
Show resolved Hide resolved
anchorOffset,
}) => {
const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
const [overlayRef, updateOverlayRef] = useRenderForcingRef<HTMLDivElement>()
Expand Down Expand Up @@ -140,6 +142,8 @@ export const AnchoredOverlay: React.FC<React.PropsWithChildren<AnchoredOverlayPr
floatingElementRef: overlayRef,
side,
align,
alignmentOffset,
anchorOffset,
},
[overlayRef.current],
)
Expand Down
12 changes: 10 additions & 2 deletions src/LabelGroup/LabelGroup.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@
"stories": [],
"props": [
{
"name": "sx",
"type": "SystemStyleObject"
"name": "overflowStyle",
"description": "How hidden tokens should be shown. `'inline'` shows the hidden tokens after the visible tokens. `'overlay'` shows all tokens in an overlay that appears on top of the visible tokens.",
"defaultValue": "",
"type": "'inline' | 'overlay'"
},
{
"name": "visibleChildCount",
"description": "How many tokens to show. `'auto'` truncates the tokens to fit in the parent container. Passing a number will truncate after that number tokens. If this is undefined, tokens will never be truncated.",
"defaultValue": "",
"type": "'auto' | number"
}
],
"subcomponents": []
Expand Down
143 changes: 143 additions & 0 deletions src/LabelGroup/LabelGroup.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React from 'react'
import styled from 'styled-components'
import LabelGroup from './LabelGroup'
import {Meta, Story} from '@storybook/react'
import Token from '../Token/Token'
import Label from '../Label/Label'

const meta: Meta = {
title: 'Components/LabelGroup/Features',
component: LabelGroup,
decorators: [
Story => {
return (
<>
<Story />
</>
)
},
],
}

const ResizableContainer = styled.div`
outline: 1px solid black;
overflow: auto;
padding: 0.25rem;
resize: horizontal;
width: 600px;
`

export const TruncateAuto: Story = () => (
<ResizableContainer>
<LabelGroup visibleChildCount="auto">
<Label>One</Label>
<Label>Two</Label>
<Label>Three</Label>
<Label>Four</Label>
<Label>Five</Label>
<Label>Six</Label>
<Label>Seven</Label>
<Label>Eight</Label>
<Label>Nine</Label>
<Label>Ten</Label>
<Label>Eleven</Label>
<Label>Twelve</Label>
<Label>Thirteen</Label>
<Label>Fourteen</Label>
<Label>Fifteen</Label>
<Label>Sixteen</Label>
</LabelGroup>
</ResizableContainer>
)

export const TruncateAutoWithInteractiveTokens: Story = () => (
<ResizableContainer>
<LabelGroup visibleChildCount="auto">
<Token as="button" text="One" />
<Token as="button" text="Two" />
<Token as="button" text="Three" />
<Token as="button" text="Four" />
<Token as="button" text="Five" />
<Token as="button" text="Six" />
<Token as="button" text="Seven" />
<Token as="button" text="Eight" />
<Token as="button" text="Nine" />
<Token as="button" text="Ten" />
<Token as="button" text="Eleven" />
<Token as="button" text="Twelve" />
<Token as="button" text="Thirteen" />
<Token as="button" text="Fourteen" />
<Token as="button" text="Fifteen" />
<Token as="button" text="Sixteen" />
</LabelGroup>
</ResizableContainer>
)

export const TruncateAfterFive: Story = () => (
<LabelGroup visibleChildCount={5}>
<Label>One</Label>
<Label>Two</Label>
<Label>Three</Label>
<Label>Four</Label>
<Label>Five</Label>
<Label>Six</Label>
<Label>Seven</Label>
<Label>Eight</Label>
<Label>Nine</Label>
<Label>Ten</Label>
<Label>Eleven</Label>
<Label>Twelve</Label>
<Label>Thirteen</Label>
<Label>Fourteen</Label>
<Label>Fifteen</Label>
<Label>Sixteen</Label>
</LabelGroup>
)

export const TruncateAutoExpandInline: Story = () => (
<ResizableContainer>
<LabelGroup visibleChildCount="auto" overflowStyle="inline">
<Label>One</Label>
<Label>Two</Label>
<Label>Three</Label>
<Label>Four</Label>
<Label>Five</Label>
<Label>Six</Label>
<Label>Seven</Label>
<Label>Eight</Label>
<Label>Nine</Label>
<Label>Ten</Label>
<Label>Eleven</Label>
<Label>Twelve</Label>
<Label>Thirteen</Label>
<Label>Fourteen</Label>
<Label>Fifteen</Label>
<Label>Sixteen</Label>
</LabelGroup>
</ResizableContainer>
)

export const TruncateAutoExpandInlineWithInteractiveTokens: Story = () => (
<ResizableContainer>
<LabelGroup visibleChildCount="auto" overflowStyle="inline">
<Token as="button" text="One" />
<Token as="button" text="Two" />
<Token as="button" text="Three" />
<Token as="button" text="Four" />
<Token as="button" text="Five" />
<Token as="button" text="Six" />
<Token as="button" text="Seven" />
<Token as="button" text="Eight" />
<Token as="button" text="Nine" />
<Token as="button" text="Ten" />
<Token as="button" text="Eleven" />
<Token as="button" text="Twelve" />
<Token as="button" text="Thirteen" />
<Token as="button" text="Fourteen" />
<Token as="button" text="Fifteen" />
<Token as="button" text="Sixteen" />
</LabelGroup>
</ResizableContainer>
)

export default meta
Loading