Skip to content

Commit 0b314ea

Browse files
authored
Rename SegmentedControl props to match selection APIs (#7097)
* Rename SegmentedControl props to match selection APIs * fix chromatic stories * ts strict * Add AriaLabelingProps * Pass value and defaultValue to RadioGroup * fix merge conflicts
1 parent 72be76a commit 0b314ea

File tree

3 files changed

+56
-46
lines changed

3 files changed

+56
-46
lines changed

packages/@react-spectrum/s2/chromatic/SegmentedControl.stories.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export default meta;
3232

3333
export const Example = (args: any) => (
3434
<SegmentedControl {...args} styles={style({width: '[250px]'})}>
35-
<SegmentedControlItem value="day">Day</SegmentedControlItem>
36-
<SegmentedControlItem value="week">Week</SegmentedControlItem>
37-
<SegmentedControlItem value="month">Month</SegmentedControlItem>
38-
<SegmentedControlItem value="year">Year</SegmentedControlItem>
35+
<SegmentedControlItem id="day">Day</SegmentedControlItem>
36+
<SegmentedControlItem id="week">Week</SegmentedControlItem>
37+
<SegmentedControlItem id="month">Month</SegmentedControlItem>
38+
<SegmentedControlItem id="year">Year</SegmentedControlItem>
3939
</SegmentedControl>
4040
);
4141

@@ -45,9 +45,9 @@ Example.args = {
4545

4646
export const WithIcons = (args: any) => (
4747
<SegmentedControl {...args} styles={style({width: '[400px]'})}>
48-
<SegmentedControlItem value="unordered"><ListBulleted /><Text>Unordered</Text></SegmentedControlItem>
49-
<SegmentedControlItem value="ordered"><ListNumbered /><Text>Ordered</Text></SegmentedControlItem>
50-
<SegmentedControlItem value="task list"><ListMultiSelect /><Text>Task List</Text></SegmentedControlItem>
48+
<SegmentedControlItem id="unordered"><ListBulleted /><Text>Unordered</Text></SegmentedControlItem>
49+
<SegmentedControlItem id="ordered"><ListNumbered /><Text>Ordered</Text></SegmentedControlItem>
50+
<SegmentedControlItem id="task list"><ListMultiSelect /><Text>Task List</Text></SegmentedControlItem>
5151
</SegmentedControl>
5252
);
5353

@@ -57,9 +57,9 @@ WithIcons.args = {
5757

5858
export const OnlyIcons = (args: any) => (
5959
<SegmentedControl styles={style({maxWidth: 'fit'})} {...args}>
60-
<SegmentedControlItem value="align bottom"><AlignBottom /></SegmentedControlItem>
61-
<SegmentedControlItem value="align center"><AlignCenter /></SegmentedControlItem>
62-
<SegmentedControlItem value="align left"><AlignLeft /></SegmentedControlItem>
60+
<SegmentedControlItem id="align bottom"><AlignBottom /></SegmentedControlItem>
61+
<SegmentedControlItem id="align center"><AlignCenter /></SegmentedControlItem>
62+
<SegmentedControlItem id="align left"><AlignLeft /></SegmentedControlItem>
6363
</SegmentedControl>
6464
);
6565

@@ -69,10 +69,10 @@ OnlyIcons.args = {
6969

7070
export const CustomWidth = (args: any) => (
7171
<SegmentedControl {...args} styles={style({width: '[400px]'})}>
72-
<SegmentedControlItem value="overview">Overview</SegmentedControlItem>
73-
<SegmentedControlItem value="specs">Specs</SegmentedControlItem>
74-
<SegmentedControlItem value="guidelines">Guidelines</SegmentedControlItem>
75-
<SegmentedControlItem value="accessibility">Accessibility</SegmentedControlItem>
72+
<SegmentedControlItem id="overview">Overview</SegmentedControlItem>
73+
<SegmentedControlItem id="specs">Specs</SegmentedControlItem>
74+
<SegmentedControlItem id="guidelines">Guidelines</SegmentedControlItem>
75+
<SegmentedControlItem id="accessibility">Accessibility</SegmentedControlItem>
7676
</SegmentedControl>
7777
);
7878

packages/@react-spectrum/s2/src/SegmentedControl.tsx

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13+
import {AriaLabelingProps, DOMRef, DOMRefValue, FocusableRef} from '@react-types/shared';
1314
import {centerBaseline} from './CenterBaseline';
14-
import {ContextValue, DEFAULT_SLOT, Provider, TextContext as RACTextContext, Radio, RadioGroup, RadioGroupProps, RadioGroupStateContext, RadioProps} from 'react-aria-components';
15+
import {ContextValue, DEFAULT_SLOT, Provider, TextContext as RACTextContext, Radio, RadioGroup, RadioGroupStateContext, SlotProps} from 'react-aria-components';
1516
import {createContext, forwardRef, ReactNode, RefObject, useCallback, useContext, useRef} from 'react';
16-
import {DOMRef, DOMRefValue, FocusableRef} from '@react-types/shared';
1717
import {focusRing, size, style} from '../style' with {type: 'macro'};
1818
import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
1919
import {IconContext} from './Icon';
@@ -23,7 +23,7 @@ import {useDOMRef, useFocusableRef} from '@react-spectrum/utils';
2323
import {useLayoutEffect} from '@react-aria/utils';
2424
import {useSpectrumContextProps} from './useSpectrumContextProps';
2525

26-
export interface SegmentedControlProps extends Omit<RadioGroupProps, 'isReadOnly' | 'name' | 'isRequired' | 'isInvalid' | 'validate' | 'validationBehavior' | 'children' | 'className' | 'style' | 'aria-label' | 'orientation'>, StyleProps{
26+
export interface SegmentedControlProps extends AriaLabelingProps, StyleProps, SlotProps {
2727
/**
2828
* The content to display in the segmented control.
2929
*/
@@ -32,16 +32,22 @@ export interface SegmentedControlProps extends Omit<RadioGroupProps, 'isReadOnly
3232
* Whether the segmented control is disabled.
3333
*/
3434
isDisabled?: boolean,
35-
/**
36-
* Defines a string value that labels the current element.
37-
*/
38-
'aria-label': string
35+
/** The id of the currently selected item (controlled). */
36+
selectedKey?: string | null,
37+
/** The id of the initial selected item (uncontrolled). */
38+
defaultSelectedKey?: string,
39+
/** Handler that is called when the selection changes. */
40+
onSelectionChange?: (id: string) => void
3941
}
40-
export interface SegmentedControlItemProps extends Omit<RadioProps, 'children' | 'className' | 'style' | 'onHoverStart' | 'onHoverEnd' | 'onHoverChange'>, StyleProps {
42+
export interface SegmentedControlItemProps extends AriaLabelingProps, StyleProps {
4143
/**
42-
* The content to display in the control item.
44+
* The content to display in the segmented control item.
4345
*/
44-
children: ReactNode
46+
children: ReactNode,
47+
/** The id of the item, matching the value used in SegmentedControl's `selectedKey` prop. */
48+
id: string,
49+
/** Whether the item is disabled or not. */
50+
isDisabled?: boolean
4551
}
4652

4753
export const SegmentedControlContext = createContext<ContextValue<SegmentedControlProps, DOMRefValue<HTMLDivElement>>>(null);
@@ -135,8 +141,9 @@ const InternalSegmentedControlContext = createContext<InternalSegmentedControlCo
135141
function SegmentedControl(props: SegmentedControlProps, ref: DOMRef<HTMLDivElement>) {
136142
[props, ref] = useSpectrumContextProps(props, ref, SegmentedControlContext);
137143
let {
138-
defaultValue,
139-
value
144+
defaultSelectedKey,
145+
selectedKey,
146+
onSelectionChange
140147
} = props;
141148
let domRef = useDOMRef(ref);
142149

@@ -148,21 +155,23 @@ function SegmentedControl(props: SegmentedControlProps, ref: DOMRef<HTMLDivEleme
148155
prevRef.current = currentSelectedRef?.current.getBoundingClientRect();
149156
}
150157

151-
if (props.onChange) {
152-
props.onChange(value);
158+
if (onSelectionChange) {
159+
onSelectionChange(value);
153160
}
154161
};
155162

156163
return (
157164
<RadioGroup
158165
{...props}
166+
value={selectedKey}
167+
defaultValue={defaultSelectedKey}
159168
ref={domRef}
160169
orientation="horizontal"
161170
style={props.UNSAFE_style}
162171
onChange={onChange}
163172
className={(props.UNSAFE_className || '') + segmentedControl({size: 'M'}, props.styles)}
164173
aria-label={props['aria-label']}>
165-
<DefaultSelectionTracker defaultValue={defaultValue} value={value} prevRef={prevRef} currentSelectedRef={currentSelectedRef}>
174+
<DefaultSelectionTracker defaultValue={defaultSelectedKey} value={selectedKey} prevRef={prevRef} currentSelectedRef={currentSelectedRef}>
166175
{props.children}
167176
</DefaultSelectionTracker>
168177
</RadioGroup>
@@ -197,15 +206,15 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
197206
let divRef = useRef<HTMLDivElement>(null);
198207
let {register, prevRef, currentSelectedRef} = useContext(InternalSegmentedControlContext);
199208
let state = useContext(RadioGroupStateContext);
200-
let isSelected = props.value === state?.selectedValue;
209+
let isSelected = props.id === state?.selectedValue;
201210
// do not apply animation if a user has the prefers-reduced-motion setting
202211
let isReduced = false;
203212
if (window?.matchMedia) {
204213
isReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
205214
}
206215

207216
useLayoutEffect(() => {
208-
register?.(props.value);
217+
register?.(props.id);
209218
}, []);
210219

211220
useLayoutEffect(() => {
@@ -231,7 +240,8 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
231240

232241
return (
233242
<Radio
234-
{...props}
243+
{...props}
244+
value={props.id}
235245
ref={domRef}
236246
inputRef={inputRef}
237247
style={props.UNSAFE_style}

packages/@react-spectrum/s2/stories/SegmentedControl.stories.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ export default meta;
3434

3535
export const Example = (args: any) => (
3636
<SegmentedControl {...args} styles={style({width: '[250px]'})}>
37-
<SegmentedControlItem value="day">Day</SegmentedControlItem>
38-
<SegmentedControlItem value="week">Week</SegmentedControlItem>
39-
<SegmentedControlItem value="month">Month</SegmentedControlItem>
40-
<SegmentedControlItem value="year">Year</SegmentedControlItem>
37+
<SegmentedControlItem id="day">Day</SegmentedControlItem>
38+
<SegmentedControlItem id="week">Week</SegmentedControlItem>
39+
<SegmentedControlItem id="month">Month</SegmentedControlItem>
40+
<SegmentedControlItem id="year">Year</SegmentedControlItem>
4141
</SegmentedControl>
4242
);
4343

@@ -47,9 +47,9 @@ Example.args = {
4747

4848
export const WithIcons = (args: any) => (
4949
<SegmentedControl {...args} styles={style({width: '[400px]'})}>
50-
<SegmentedControlItem value="unordered"><ListBulleted /><Text>Unordered</Text></SegmentedControlItem>
51-
<SegmentedControlItem value="ordered"><ListNumbered /><Text>Ordered</Text></SegmentedControlItem>
52-
<SegmentedControlItem value="task list"><ListMultiSelect /><Text>Task List</Text></SegmentedControlItem>
50+
<SegmentedControlItem id="unordered"><ListBulleted /><Text>Unordered</Text></SegmentedControlItem>
51+
<SegmentedControlItem id="ordered"><ListNumbered /><Text>Ordered</Text></SegmentedControlItem>
52+
<SegmentedControlItem id="task list"><ListMultiSelect /><Text>Task List</Text></SegmentedControlItem>
5353
</SegmentedControl>
5454
);
5555

@@ -59,9 +59,9 @@ WithIcons.args = {
5959

6060
export const OnlyIcons = (args: any) => (
6161
<SegmentedControl {...args}>
62-
<SegmentedControlItem value="align bottom"><AlignBottom /></SegmentedControlItem>
63-
<SegmentedControlItem value="align center"><AlignCenter /></SegmentedControlItem>
64-
<SegmentedControlItem value="align left"><AlignLeft /></SegmentedControlItem>
62+
<SegmentedControlItem id="align bottom"><AlignBottom /></SegmentedControlItem>
63+
<SegmentedControlItem id="align center"><AlignCenter /></SegmentedControlItem>
64+
<SegmentedControlItem id="align left"><AlignLeft /></SegmentedControlItem>
6565
</SegmentedControl>
6666
);
6767

@@ -71,10 +71,10 @@ OnlyIcons.args = {
7171

7272
export const CustomWidth = (args: any) => (
7373
<SegmentedControl {...args} styles={style({width: '[400px]'})}>
74-
<SegmentedControlItem value="overview">Overview</SegmentedControlItem>
75-
<SegmentedControlItem value="specs">Specs</SegmentedControlItem>
76-
<SegmentedControlItem value="guidelines">Guidelines</SegmentedControlItem>
77-
<SegmentedControlItem value="accessibility">Accessibility</SegmentedControlItem>
74+
<SegmentedControlItem id="overview">Overview</SegmentedControlItem>
75+
<SegmentedControlItem id="specs">Specs</SegmentedControlItem>
76+
<SegmentedControlItem id="guidelines">Guidelines</SegmentedControlItem>
77+
<SegmentedControlItem id="accessibility">Accessibility</SegmentedControlItem>
7878
</SegmentedControl>
7979
);
8080

0 commit comments

Comments
 (0)