Skip to content

Commit daf749e

Browse files
authored
Merge branch 'main' into jclem/remove-duplicate-dep
2 parents 5e679ce + 9ce6493 commit daf749e

31 files changed

+1923
-645
lines changed

.changeset/bright-timers-jog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': minor
3+
---
4+
5+
Adds responsive behavior to SegmentedControl's `fullWidth` prop.

.changeset/hip-buses-eat.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+
Change `createSlots` to use layout effects when registering slots

.changeset/old-experts-applaud.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
- Fixes `role` and keyboard behavior for SegmentedControl.
6+
- Fixes a bug where icon-only SegmentedControl buttons did not fill the parent width when the `fullWidth` prop was set
7+
- Fixes a bug where click handlers were not passed correctly when the responsive variant was set to `'hideLabels'`

.changeset/wicked-otters-warn.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+
Overlay documentation fixes

docs/content/Overlay.mdx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,13 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T
7272
7373
## Positioning
7474
75-
`Overlay` renders its `children` within a div positioned absolutely within a portal within the default portal root. The overlay will not update its positioning if the portal root's positioning changes (e.g., if the portal root is statically positioned after some DOM element that dynamically resizes). You may consider [customizing the portal or specifying a different portal root](/Portal#customizing-the-portal-root) to achieve different positioning behavior.
75+
`Overlay` renders its `children` within a div positioned absolutely within a portal within the default portal root. The overlay will not update its positioning if the portal root's positioning changes (e.g., if the portal root is statically positioned after some DOM element that dynamically resizes). You may consider using the [AnchoredOverlay](/AnchoredOverlay) component or [customizing the portal root](/Portal#customizing-the-portal-root) to achieve different positioning behavior.
7676
7777
## Props
7878
7979
### Overlay
8080
8181
<PropsTable>
82-
<PropsTableRow
83-
required
84-
name="anchorRef"
85-
type="React.RefObject<HTMLElement>"
86-
description={
87-
<>
88-
Element the <InlineCode>Overlay</InlineCode> should be anchored to.
89-
</>
90-
}
91-
/>
9282
<PropsTableRow
9383
required
9484
name="returnFocusRef"
@@ -182,6 +172,23 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T
182172
</>
183173
}
184174
/>
175+
<PropsTableRow
176+
name="maxHeight"
177+
type={`| 'xsmall'
178+
| 'small'
179+
| 'medium'
180+
| 'large'
181+
| 'xlarge'`}
182+
description={
183+
<>
184+
Sets the maximum height of the <InlineCode>Overlay</InlineCode>, pick from our set list of heights.
185+
<InlineCode>xsmall</InlineCode> corresponds to <InlineCode>192px</InlineCode>, <InlineCode>small</InlineCode> corresponds
186+
to <InlineCode>256px</InlineCode>, <InlineCode>medium</InlineCode> corresponds to <InlineCode>320px</InlineCode>,{' '}
187+
<InlineCode>large</InlineCode> corresponds to <InlineCode>432px</InlineCode>, <InlineCode>xlarge</InlineCode> corresponds
188+
to <InlineCode>600px</InlineCode>.
189+
</>
190+
}
191+
/>
185192
<PropsTableRow
186193
name="visibility"
187194
type={`| 'visible'

docs/content/SegmentedControl.mdx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,16 @@ description: Use a segmented control to let users select an option from a short
151151
<PropsTableRow name="aria-label" type="string" />
152152
<PropsTableRow name="aria-labelledby" type="string" />
153153
<PropsTableRow name="aria-describedby" type="string" />
154-
<PropsTableRow name="fullWidth" type="boolean" description="Whether the control fills the width of its parent" />
154+
<PropsTableRow
155+
name="fullWidth"
156+
type={`| boolean
157+
| {
158+
narrow?: boolean
159+
regular?: boolean
160+
wide?: boolean
161+
}`}
162+
description="Whether the control fills the width of its parent"
163+
/>
155164
<PropsTableRow name="loading" type="boolean" description="Whether the selected segment is being calculated" />
156165
<PropsTableRow
157166
name="onChange"
@@ -161,10 +170,12 @@ description: Use a segmented control to let users select an option from a short
161170
/>
162171
<PropsTableRow
163172
name="variant"
164-
type="'default' | {
165-
narrow?: 'hideLabels' | 'dropdown' | 'default'
166-
regular?: 'hideLabels' | 'dropdown' | 'default'
167-
}"
173+
type={`| 'default'
174+
| {
175+
narrow?: 'hideLabels' | 'dropdown' | 'default'
176+
regular?: 'hideLabels' | 'dropdown' | 'default'
177+
wide?: 'hideLabels' | 'dropdown' | 'default'
178+
}`}
168179
defaultValue="'default'"
169180
description="Configure alternative ways to render the control when it gets rendered in tight spaces"
170181
/>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
title: UnderlineNav v2
3+
status: Draft
4+
description: Use an underlined nav to allow tab like navigation with overflow behaviour in your UI.
5+
---
6+
7+
## Examples
8+
9+
### Simple
10+
11+
```jsx live drafts
12+
<UnderlineNav label="simple nav">
13+
<UnderlineNav.Link selected>Item 1</UnderlineNav.Link>
14+
<UnderlineNav.Link>Item 2</UnderlineNav.Link>
15+
<UnderlineNav.Link>Item 3</UnderlineNav.Link>
16+
</UnderlineNav>
17+
```
18+
19+
### With icons
20+
21+
```jsx live drafts
22+
<UnderlineNav label="simple nav with icons">
23+
<UnderlineNav.Link selected leadingIcon={EyeIcon}>
24+
Item 1
25+
</UnderlineNav.Link>
26+
<UnderlineNav.Link>Item 2</UnderlineNav.Link>
27+
</UnderlineNav>
28+
```
29+
30+
## Props
31+
32+
### UnderlineNav
33+
34+
<PropsTable>
35+
<PropsTableRow name="aria-label" type="string" />
36+
<PropsTableRow name="aria-labelledby" type="string" />
37+
<PropsTableRow name="aria-describedby" type="string" />
38+
<PropsTableRow
39+
name="overflow"
40+
type="'auto' | 'menu' | 'scroll'"
41+
description="Controls the type of overflow behaviour in smaller screens"
42+
/>
43+
<PropsTableRow name="align" type="right | left" description="The alignment of the nav links" />
44+
<PropsTableRow
45+
name="afterSelect"
46+
type="(event) => void"
47+
description="The handler that gets called when a nav link child is selected"
48+
/>
49+
<PropsTableSxRow />
50+
</PropsTable>
51+
52+
### UnderlineNav.Link
53+
54+
<PropsTable>
55+
<PropsTableRow name="leadingIcon" type="Component" description="The leading icon comes before item label" />
56+
<PropsTableRow name="selected" type="boolean" description="Whether the link is selected" />
57+
<PropsTableRow
58+
name="onSelect"
59+
type="(event) => void"
60+
description="The handler that gets called when a nav link is selected"
61+
/>
62+
<PropsTableRow name="as" type="string | Component" description="What kind of component needs to be rendered" />
63+
<PropsTableSxRow />
64+
</PropsTable>
65+
66+
## Status
67+
68+
<ComponentChecklist
69+
items={{
70+
propsDocumented: true,
71+
noUnnecessaryDeps: false,
72+
adaptsToThemes: false,
73+
adaptsToScreenSizes: false,
74+
fullTestCoverage: false,
75+
usedInProduction: false,
76+
usageExamplesDocumented: false,
77+
hasStorybookStories: false,
78+
designReviewed: false,
79+
a11yReviewed: false,
80+
stableApi: false,
81+
addressedApiFeedback: false,
82+
hasDesignGuidelines: false,
83+
hasFigmaComponent: false
84+
}}
85+
/>

docs/src/@primer/gatsby-theme-doctocat/nav.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
url: /ActionList
3939
- title: ActionMenu
4040
url: /ActionMenu
41+
- title: AnchoredOverlay
42+
url: /AnchoredOverlay
4143
- title: Autocomplete
4244
url: /Autocomplete
4345
- title: Avatar

src/SegmentedControl/SegmentedControl.test.tsx

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {SegmentedControl} from '.' // TODO: update import when we move this to t
99
import theme from '../theme'
1010
import {BaseStyles, SSRProvider, ThemeProvider} from '..'
1111
import {act} from 'react-test-renderer'
12-
import {viewportRanges} from '../hooks/useMatchMedia'
12+
import {viewportRanges} from '../hooks/useResponsiveValue'
1313

1414
const segmentData = [
1515
{label: 'Preview', id: 'preview', iconLabel: 'EyeIcon', icon: () => <EyeIcon aria-label="EyeIcon" />},
@@ -197,55 +197,6 @@ describe('SegmentedControl', () => {
197197
expect(handleClick).toHaveBeenCalled()
198198
})
199199

200-
it('focuses the selected button first', async () => {
201-
const user = userEvent.setup()
202-
const {getByRole} = render(
203-
<>
204-
<button>Before</button>
205-
<SegmentedControl aria-label="File view">
206-
{segmentData.map(({label, id}, index) => (
207-
<SegmentedControl.Button selected={index === 1} key={label} id={id}>
208-
{label}
209-
</SegmentedControl.Button>
210-
))}
211-
</SegmentedControl>
212-
</>
213-
)
214-
const initialFocusButtonNode = getByRole('button', {name: segmentData[1].label})
215-
216-
expect(document.activeElement?.id).not.toEqual(initialFocusButtonNode.id)
217-
218-
await user.tab() // focus the button before the segmented control
219-
await user.tab() // move focus into the segmented control
220-
221-
expect(document.activeElement?.id).toEqual(initialFocusButtonNode.id)
222-
})
223-
224-
it('focuses the previous button when keying ArrowLeft, and the next button when keying ArrowRight', () => {
225-
const {getByRole} = render(
226-
<SegmentedControl aria-label="File view">
227-
{segmentData.map(({label, id}, index) => (
228-
<SegmentedControl.Button selected={index === 1} key={label} id={id}>
229-
{label}
230-
</SegmentedControl.Button>
231-
))}
232-
</SegmentedControl>
233-
)
234-
const initialFocusButtonNode = getByRole('button', {name: segmentData[1].label})
235-
const nextFocusButtonNode = getByRole('button', {name: segmentData[0].label})
236-
237-
expect(document.activeElement?.id).not.toEqual(nextFocusButtonNode.id)
238-
239-
fireEvent.focus(initialFocusButtonNode)
240-
fireEvent.keyDown(initialFocusButtonNode, {key: 'ArrowLeft'})
241-
242-
expect(document.activeElement?.id).toEqual(nextFocusButtonNode.id)
243-
244-
fireEvent.keyDown(initialFocusButtonNode, {key: 'ArrowRight'})
245-
246-
expect(document.activeElement?.id).toEqual(initialFocusButtonNode.id)
247-
})
248-
249200
it('calls onChange with index of clicked segment button when using the dropdown variant', async () => {
250201
act(() => {
251202
matchMedia.useMediaQuery(viewportRanges.narrow)

0 commit comments

Comments
 (0)