Skip to content

Commit 574d2c8

Browse files
authored
Merge branch 'main' into treeview-arrow-keys
2 parents b9b4415 + 4a40085 commit 574d2c8

File tree

9 files changed

+92
-14
lines changed

9 files changed

+92
-14
lines changed

.changeset/large-trains-press.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 a `size` prop to the SegmentedControl component. Users can choose between 'medium' (default), and 'small'. More sizes can be added when/if we find we need them.

docs/content/PageLayout.mdx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,39 @@ Add `offsetHeader` prop to specify the height of the custom sticky header along
202202
</Box>
203203
```
204204

205+
### With `NavList`
206+
207+
`PageLayout.Pane` by itself does not specify or provide landmark roles. Using
208+
components that define their own landmark roles, such as [`NavList`](/NavList), will provide
209+
clear structure to the page that is not present by default in `PageLayout.Pane`.
210+
211+
It's important to note that multiple landmark roles, such as multiple
212+
`<nav>` elements, should be uniquely labelled in order to clarify what the
213+
navigation container is used for.
214+
215+
```jsx live
216+
<PageLayout>
217+
<PageLayout.Header>
218+
<Placeholder label="Header" height={64} />
219+
</PageLayout.Header>
220+
<PageLayout.Pane position="start" aria-label="Secondary navigation">
221+
<NavList>
222+
<NavList.Item href="/" aria-current="page">
223+
Home
224+
</NavList.Item>
225+
<NavList.Item href="/about">About</NavList.Item>
226+
<NavList.Item href="/contact">Contact</NavList.Item>
227+
</NavList>
228+
</PageLayout.Pane>
229+
<PageLayout.Content>
230+
<Placeholder label="Content" height={240} />
231+
</PageLayout.Content>
232+
<PageLayout.Footer>
233+
<Placeholder label="Footer" height={64} />
234+
</PageLayout.Footer>
235+
</PageLayout>
236+
```
237+
205238
### With `aria-label`
206239

207240
Using `aria-label` along with `PageLayout.Header`, `PageLayout.Content`, or `PageLayout.Footer` creates a unique label for that landmark role that can make it easier to navigate between sections of content on a page.
@@ -262,6 +295,20 @@ Each component may be labeled through either `aria-label` or `aria-labelledby` i
262295
- [WCAG, ARIA11 Technique](https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA11)
263296
- [MDN, Landmark roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/landmark_role)
264297

298+
### `PageLayout.Pane`
299+
300+
The `PageLayout.Pane` component does not provide a default landmark role as the
301+
content of this component may have different roles. When using this component,
302+
consider the type of content being rendered inside of `PageLayout.Pane` and if
303+
it requires a landmark role. Common landmark roles include:
304+
305+
- [`aside`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
306+
- [`navigation`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
307+
308+
Some components, such as `NavList` may already include a landmark role. In these
309+
situation, these components use an appropriate landmark role and no further
310+
action is needed when using `PageLayout.Pane`.
311+
265312
### Screen readers
266313

267314
Most screen readers provide a mechanism through which you can navigate between landmarks on the page. Typically, this is done through a specific keyboard shortcut or through an option in a rotor.

docs/content/SegmentedControl.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ const Controlled = () => {
3939
render(Controlled)
4040
```
4141

42+
### Small
43+
44+
```jsx live
45+
<SegmentedControl aria-label="File view" size="small">
46+
<SegmentedControl.Button defaultSelected>Preview</SegmentedControl.Button>
47+
<SegmentedControl.Button>Raw</SegmentedControl.Button>
48+
<SegmentedControl.Button>Blame</SegmentedControl.Button>
49+
</SegmentedControl>
50+
```
51+
4252
### With icons and labels
4353

4454
```jsx live
@@ -210,6 +220,7 @@ render(Controlled)
210220
type="boolean"
211221
description="Whether the segment is selected. This is used for controlled SegmentedControls, and needs to be updated using the onChange handler on SegmentedControl."
212222
/>
223+
<PropsTableRow name="selected" type="'small' | 'medium'" description="The size of the buttons" />
213224
<PropsTableRow
214225
name="defaultSelected"
215226
type="boolean"

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/PageLayout/PageLayout.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ export const CustomStickyHeader: Story = args => (
602602
// a box to create a sticky top element that will be on the consumer side and outside of the PageLayout component
603603
<Box data-testid="story-window">
604604
<Box
605+
as="header"
605606
data-testid="sticky-header"
606607
sx={{
607608
position: 'sticky',

src/SegmentedControl/SegmentedControl.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,24 @@ type SegmentedControlProps = {
2424
fullWidth?: boolean | ResponsiveValue<boolean>
2525
/** The handler that gets called when a segment is selected */
2626
onChange?: (selectedIndex: number) => void
27+
/** The size of the buttons */
28+
size?: 'small' | 'medium'
2729
/** Configure alternative ways to render the control when it gets rendered in tight spaces */
2830
variant?: 'default' | Partial<Record<WidthOnlyViewportRangeKeys, 'hideLabels' | 'dropdown' | 'default'>>
2931
} & SxProp
3032

31-
const getSegmentedControlStyles = (isFullWidth?: boolean) => ({
33+
const getSegmentedControlStyles = (props: {isFullWidth?: boolean; size?: SegmentedControlProps['size']}) => ({
3234
backgroundColor: 'segmentedControl.bg',
3335
borderColor: 'border.default',
3436
borderRadius: 2,
3537
borderStyle: 'solid',
3638
borderWidth: 1,
37-
display: isFullWidth ? 'flex' : 'inline-flex',
38-
height: '32px', // TODO: use primitive `control.medium.size` when it is available
39+
display: props.isFullWidth ? 'flex' : 'inline-flex',
40+
fontSize: props.size === 'small' ? 0 : 1,
41+
height: props.size === 'small' ? '28px' : '32px', // TODO: use primitive `control.{small|medium}.size` when it is available
3942
margin: 0,
4043
padding: 0,
41-
width: isFullWidth ? '100%' : undefined
44+
width: props.isFullWidth ? '100%' : undefined
4245
})
4346

4447
const Root: React.FC<React.PropsWithChildren<SegmentedControlProps>> = ({
@@ -47,6 +50,7 @@ const Root: React.FC<React.PropsWithChildren<SegmentedControlProps>> = ({
4750
children,
4851
fullWidth,
4952
onChange,
53+
size,
5054
sx: sxProp = {},
5155
variant,
5256
...rest
@@ -91,7 +95,7 @@ const Root: React.FC<React.PropsWithChildren<SegmentedControlProps>> = ({
9195

9296
return React.isValidElement<SegmentedControlIconButtonProps>(childArg) ? childArg.props['aria-label'] : null
9397
}
94-
const listSx = merge(getSegmentedControlStyles(isFullWidth), sxProp as SxProp)
98+
const listSx = merge(getSegmentedControlStyles({isFullWidth, size}), sxProp as SxProp)
9599

96100
if (!ariaLabel && !ariaLabelledby) {
97101
// eslint-disable-next-line no-console

src/SegmentedControl/__snapshots__/SegmentedControl.test.tsx.snap

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ exports[`SegmentedControl renders consistently 1`] = `
1010
flex-grow: 1;
1111
margin-top: -1px;
1212
margin-bottom: -1px;
13-
height: 32px;
1413
--separator-color: transparent;
1514
}
1615
@@ -45,7 +44,6 @@ exports[`SegmentedControl renders consistently 1`] = `
4544
flex-grow: 1;
4645
margin-top: -1px;
4746
margin-bottom: -1px;
48-
height: 32px;
4947
--separator-color: #d0d7de;
5048
}
5149
@@ -82,7 +80,7 @@ exports[`SegmentedControl renders consistently 1`] = `
8280
color: currentColor;
8381
cursor: pointer;
8482
font-family: inherit;
85-
font-size: 14px;
83+
font-size: inherit;
8684
font-weight: 600;
8785
padding: 0;
8886
height: 100%;
@@ -145,7 +143,7 @@ exports[`SegmentedControl renders consistently 1`] = `
145143
color: currentColor;
146144
cursor: pointer;
147145
font-family: inherit;
148-
font-size: 14px;
146+
font-size: inherit;
149147
font-weight: 400;
150148
padding: var(--segmented-control-button-bg-inset);
151149
height: 100%;
@@ -216,7 +214,7 @@ exports[`SegmentedControl renders consistently 1`] = `
216214
color: currentColor;
217215
cursor: pointer;
218216
font-family: inherit;
219-
font-size: 14px;
217+
font-size: inherit;
220218
font-weight: 400;
221219
padding: var(--segmented-control-button-bg-inset);
222220
height: 100%;
@@ -286,6 +284,7 @@ exports[`SegmentedControl renders consistently 1`] = `
286284
display: -webkit-inline-flex;
287285
display: -ms-inline-flexbox;
288286
display: inline-flex;
287+
font-size: 14px;
289288
height: 32px;
290289
margin: 0;
291290
padding: 0;

src/SegmentedControl/examples.stories.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Args = {
1212
fullWidthAtNarrow?: boolean
1313
fullWidthAtRegular?: boolean
1414
fullWidthAtWide?: boolean
15+
size?: 'small' | 'medium'
1516
variantAtNarrow: ResponsiveVariantOptions
1617
variantAtRegular: ResponsiveVariantOptions
1718
variantAtWide: ResponsiveVariantOptions
@@ -72,6 +73,13 @@ export default {
7273
type: 'boolean'
7374
}
7475
},
76+
size: {
77+
defaultValue: 'medium',
78+
control: {
79+
type: 'radio',
80+
options: ['small', 'medium']
81+
}
82+
},
7583
variantAtNarrow: {
7684
name: 'variant.narrow',
7785
defaultValue: 'default',
@@ -116,6 +124,7 @@ export const Default = (args: Args) => (
116124
aria-label="File view"
117125
fullWidth={parseFullWidthFromArgs(args)}
118126
variant={parseVariantFromArgs(args)}
127+
size={args.size}
119128
>
120129
<SegmentedControl.Button selected>Preview</SegmentedControl.Button>
121130
<SegmentedControl.Button>Raw</SegmentedControl.Button>
@@ -135,6 +144,7 @@ export const Controlled = (args: Args) => {
135144
onChange={handleChange}
136145
fullWidth={parseFullWidthFromArgs(args)}
137146
variant={parseVariantFromArgs(args)}
147+
size={args.size}
138148
>
139149
<SegmentedControl.Button selected={selectedIndex === 0}>Preview</SegmentedControl.Button>
140150
<SegmentedControl.Button selected={selectedIndex === 1}>Raw</SegmentedControl.Button>
@@ -151,6 +161,7 @@ export const WithIconsAndLabels = (args: Args) => (
151161
aria-label="File view"
152162
fullWidth={parseFullWidthFromArgs(args)}
153163
variant={parseVariantFromArgs(args)}
164+
size={args.size}
154165
>
155166
<SegmentedControl.Button selected leadingIcon={EyeIcon}>
156167
Preview
@@ -169,6 +180,7 @@ export const IconsOnly = (args: Args) => (
169180
aria-label="File view"
170181
fullWidth={parseFullWidthFromArgs(args)}
171182
variant={parseVariantFromArgs(args)}
183+
size={args.size}
172184
>
173185
<SegmentedControl.IconButton selected icon={EyeIcon} aria-label="Preview" />
174186
<SegmentedControl.IconButton icon={FileCodeIcon} aria-label="Raw" />

src/SegmentedControl/getSegmentedControlStyles.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const getSegmentedControlButtonStyles = (
3737
color: 'currentColor',
3838
cursor: 'pointer',
3939
fontFamily: 'inherit',
40-
fontSize: 1,
40+
fontSize: 'inherit',
4141
fontWeight: props?.selected ? 'bold' : 'normal',
4242
padding: props?.selected ? 0 : 'var(--segmented-control-button-bg-inset)',
4343
height: '100%',
@@ -114,7 +114,6 @@ export const getSegmentedControlListItemStyles = () => ({
114114
flexGrow: 1,
115115
marginTop: '-1px',
116116
marginBottom: '-1px',
117-
height: '32px', // TODO: use primitive `control.medium.size` when it is available
118117
':not(:last-child)': borderedSegment,
119118
...directChildLayoutAdjustments
120119
})

0 commit comments

Comments
 (0)