Skip to content

Commit f30983a

Browse files
authored
Add slot contexts to all S2 components (#6856)
1 parent c4a783e commit f30983a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+636
-392
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
*/
1212

1313
import {baseColor, fontRelative, style} from '../style/spectrum-theme' with { type: 'macro' };
14-
import {ButtonProps, ButtonRenderProps, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components';
14+
import {ButtonProps, ButtonRenderProps, ContextValue, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components';
1515
import {centerBaseline} from './CenterBaseline';
16-
import {FocusableRef} from '@react-types/shared';
16+
import {createContext, forwardRef, ReactNode, useContext} from 'react';
17+
import {FocusableRef, FocusableRefValue} from '@react-types/shared';
1718
import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with { type: 'macro' };
18-
import {forwardRef, ReactNode, useContext} from 'react';
1919
import {IconContext} from './Icon';
2020
import {pressScale} from './pressScale';
2121
import {TextContext} from './Content';
2222
import {useFocusableRef} from '@react-spectrum/utils';
23+
import {useSpectrumContextProps} from './useSpectrumContextProps';
2324

2425
export interface ActionButtonStyleProps {
2526
/**
@@ -170,7 +171,10 @@ export const btnStyles = style<ButtonRenderProps & ActionButtonStyleProps & Togg
170171
disableTapHighlight: true
171172
}, getAllowedOverrides());
172173

174+
export const ActionButtonContext = createContext<ContextValue<ActionButtonProps, FocusableRefValue<HTMLButtonElement>>>(null);
175+
173176
function ActionButton(props: ActionButtonProps, ref: FocusableRef<HTMLButtonElement>) {
177+
[props, ref] = useSpectrumContextProps(props, ref, ActionButtonContext);
174178
let domRef = useFocusableRef(ref);
175179
let overlayTriggerState = useContext(OverlayTriggerStateContext);
176180

@@ -189,9 +193,9 @@ function ActionButton(props: ActionButtonProps, ref: FocusableRef<HTMLButtonElem
189193
}, props.styles)}>
190194
<Provider
191195
values={[
192-
[TextContext, {className: style({paddingY: '--labelPadding', order: 1, truncate: true})}],
196+
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1, truncate: true})}],
193197
[IconContext, {
194-
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
198+
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
195199
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
196200
}]
197201
]}>

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
*/
1212

1313
import {ActionButton, ActionButtonProps} from './ActionButton';
14-
import {AriaLabelingProps, DOMProps, FocusableRef} from '@react-types/shared';
14+
import {AriaLabelingProps, DOMProps, FocusableRef, FocusableRefValue} from '@react-types/shared';
15+
import {ContextValue} from 'react-aria-components';
16+
import {createContext, forwardRef} from 'react';
1517
import {filterDOMProps} from '@react-aria/utils';
16-
import {forwardRef} from 'react';
1718
import {forwardRefType} from './types';
1819
import {Menu, MenuProps, MenuTrigger, MenuTriggerProps} from './Menu';
1920
import MoreIcon from '../s2wf-icons/S2_Icon_More_20_N.svg';
2021
import {StyleProps} from './style-utils' with { type: 'macro' };
22+
import {useSpectrumContextProps} from './useSpectrumContextProps';
2123

2224
export interface ActionMenuProps<T> extends
2325
Pick<MenuTriggerProps, 'isOpen' | 'defaultOpen' | 'onOpenChange' | 'align' | 'direction' | 'shouldFlip'>,
@@ -26,7 +28,10 @@ export interface ActionMenuProps<T> extends
2628
StyleProps, DOMProps, AriaLabelingProps {
2729
}
2830

31+
export const ActionMenuContext = createContext<ContextValue<ActionMenuProps<any>, FocusableRefValue<HTMLButtonElement>>>(null);
32+
2933
function ActionMenu<T extends object>(props: ActionMenuProps<T>, ref: FocusableRef<HTMLButtonElement>) {
34+
[props, ref] = useSpectrumContextProps(props, ref, ActionMenuContext);
3035
let buttonProps = filterDOMProps(props, {labelable: true});
3136

3237
// size independently controlled?

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

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

13-
import {ContextValue, useContextProps} from 'react-aria-components';
13+
import {ContextValue} from 'react-aria-components';
1414
import {createContext, forwardRef} from 'react';
15-
import {DOMProps, DOMRef} from '@react-types/shared';
15+
import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
1616
import {filterDOMProps} from '@react-aria/utils';
1717
import {getAllowedOverrides, StyleProps, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'};
1818
import {style} from '../style/spectrum-theme' with { type: 'macro' };
1919
import {useDOMRef} from '@react-spectrum/utils';
20+
import {useSpectrumContextProps} from './useSpectrumContextProps';
2021

2122
export interface AvatarProps extends StyleProps, DOMProps {
2223
/** Text description of the avatar. */
@@ -40,11 +41,11 @@ const imageStyles = style({
4041
disableTapHighlight: true
4142
}, getAllowedOverrides({height: true}));
4243

43-
export const AvatarContext = createContext<ContextValue<AvatarContextProps, HTMLImageElement>>({});
44+
export const AvatarContext = createContext<ContextValue<AvatarContextProps, DOMRefValue<HTMLImageElement>>>(null);
4445

4546
function Avatar(props: AvatarProps, ref: DOMRef<HTMLImageElement>) {
47+
[props, ref] = useSpectrumContextProps(props, ref, AvatarContext);
4648
let domRef = useDOMRef(ref);
47-
[props, domRef] = useContextProps(props, domRef, AvatarContext);
4849
let {
4950
alt = '',
5051
src,

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

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

13-
import {AriaLabelingProps, DOMProps, DOMRef} from '@react-types/shared';
13+
import {AriaLabelingProps, DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
1414
import {centerBaseline} from './CenterBaseline';
1515
import {centerPadding, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
16+
import {ContextValue, Provider, SlotProps} from 'react-aria-components';
1617
import {filterDOMProps} from '@react-aria/utils';
1718
import {fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'};
1819
import {IconContext} from './Icon';
19-
import {Provider} from 'react-aria-components';
20-
import React, {forwardRef, ReactNode} from 'react';
20+
import React, {createContext, forwardRef, ReactNode} from 'react';
2121
import {Text, TextContext} from './Content';
2222
import {useDOMRef} from '@react-spectrum/utils';
23+
import {useSpectrumContextProps} from './useSpectrumContextProps';
2324

2425
export interface BadgeStyleProps {
2526
/**
@@ -36,13 +37,15 @@ export interface BadgeStyleProps {
3637
variant: 'accent' | 'informative' | 'neutral' | 'positive' | 'notice' | 'negative' | 'gray' | 'red' | 'orange' | 'yellow' | 'charteuse' | 'celery' | 'green' | 'seafoam' | 'cyan' | 'blue' | 'indigo' | 'purple' | 'fuchsia' | 'magenta' | 'pink' | 'turquoise' | 'brown' | 'cinnamon' | 'silver'
3738
}
3839

39-
export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps{
40+
export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps, SlotProps {
4041
/**
4142
* The content to display in the badge.
4243
*/
4344
children: ReactNode
4445
}
4546

47+
export const BadgeContext = createContext<ContextValue<Partial<BadgeProps>, DOMRefValue<HTMLDivElement>>>(null);
48+
4649
const badge = style<BadgeStyleProps>({
4750
display: 'flex',
4851
font: 'control',
@@ -115,6 +118,7 @@ const badge = style<BadgeStyleProps>({
115118
}, getAllowedOverrides());
116119

117120
function Badge(props: BadgeProps, ref: DOMRef<HTMLDivElement>) {
121+
[props, ref] = useSpectrumContextProps(props, ref, BadgeContext);
118122
let {
119123
children,
120124
variant = 'neutral',
@@ -127,9 +131,9 @@ function Badge(props: BadgeProps, ref: DOMRef<HTMLDivElement>) {
127131
return (
128132
<Provider
129133
values={[
130-
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
134+
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
131135
[IconContext, {
132-
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
136+
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
133137
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
134138
}]
135139
]}>

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

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

13-
import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs} from 'react-aria-components';
13+
import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, ContextValue, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs, useSlottedContext} from 'react-aria-components';
1414
import {AriaBreadcrumbItemProps} from 'react-aria';
1515
import ChevronIcon from '../ui-icons/Chevron';
16-
import {Children, cloneElement, createContext, forwardRef, isValidElement, ReactElement, ReactNode, useContext, useRef} from 'react';
16+
import {createContext, forwardRef, ReactNode, useRef} from 'react';
17+
import {DOMRefValue, LinkDOMProps} from '@react-types/shared';
1718
import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
1819
import {forwardRefType} from './types';
19-
import {LinkDOMProps} from '@react-types/shared';
2020
import {size, style} from '../style/spectrum-theme' with { type: 'macro' };
2121

2222
interface BreadcrumbsStyleProps {
@@ -41,6 +41,8 @@ export interface BreadcrumbsProps<T> extends Omit<AriaBreadcrumbsProps<T>, 'chil
4141
children?: ReactNode
4242
}
4343

44+
export const BreadcrumbsContext = createContext<ContextValue<BreadcrumbsProps<any>, DOMRefValue<HTMLDivElement>>>(null);
45+
4446
const wrapper = style<BreadcrumbsStyleProps>({
4547
display: 'flex',
4648
justifyContent: 'start',
@@ -67,38 +69,27 @@ const wrapper = style<BreadcrumbsStyleProps>({
6769
}
6870
}, getAllowedOverrides());
6971

70-
const BreadcrumbsInternalContext = createContext<BreadcrumbsProps<any> & {length: number}>({length: 0});
71-
72-
function Breadcrumbs<T extends object>({
72+
function Breadcrumbs<T extends object>(props: BreadcrumbsProps<T>) {
73+
let {
7374
UNSAFE_className = '',
7475
UNSAFE_style,
7576
styles,
76-
...props
77-
}: BreadcrumbsProps<T>) {
78-
let {size = 'M', isDisabled} = props;
77+
size = 'M',
78+
children,
79+
...otherProps
80+
} = props;
7981
let ref = useRef(null);
80-
// TODO: Remove when https://github.com/adobe/react-spectrum/pull/6440 is released
81-
let childArray: ReactElement[] = [];
82-
Children.forEach(props.children, (child, index) => {
83-
if (isValidElement<{index: number}>(child)) {
84-
child = cloneElement(child, {key: index, index});
85-
childArray.push(child);
86-
}
87-
});
8882
return (
8983
<RACBreadcrumbs
90-
{...props}
84+
{...otherProps}
9185
ref={ref}
9286
style={UNSAFE_style}
9387
className={UNSAFE_className + wrapper({
9488
size
9589
}, styles)}>
96-
<Provider
97-
values={[
98-
[BreadcrumbsInternalContext, {size, isDisabled, length: childArray.length}]
99-
]}>
100-
{childArray}
101-
</Provider>
90+
<BreadcrumbsContext.Provider value={props}>
91+
{children}
92+
</BreadcrumbsContext.Provider>
10293
</RACBreadcrumbs>
10394
);
10495
}
@@ -192,43 +183,43 @@ export interface BreadcrumbProps extends Omit<AriaBreadcrumbItemProps, 'children
192183

193184
export function Breadcrumb({children, ...props}: BreadcrumbProps) {
194185
let {href, target, rel, download, ping, referrerPolicy, ...other} = props;
195-
let {size = 'M', length, isDisabled} = useContext(BreadcrumbsInternalContext);
186+
let {size = 'M', isDisabled} = useSlottedContext(BreadcrumbsContext)!;
196187
let ref = useRef(null);
197-
// TODO: use isCurrent render prop when https://github.com/adobe/react-spectrum/pull/6440 is released
198-
let isCurrent = (props as BreadcrumbProps & {index: number}).index === length - 1;
199188
return (
200189
<AriaBreadcrumb
201190
{...other}
202191
ref={ref}
203-
className={breadcrumbStyles({size, isCurrent})} >
204-
{isCurrent ?
205-
<span
206-
className={currentStyles({size})}>
207-
<Provider
208-
values={[
209-
[HeadingContext, {className: heading}]
210-
]}>
211-
{children}
212-
</Provider>
213-
</span>
214-
: (
215-
<>
216-
<Link
217-
style={({isFocusVisible}) => ({clipPath: isFocusVisible ? 'none' : 'margin-box'})}
218-
href={href}
219-
target={target}
220-
rel={rel}
221-
download={download}
222-
ping={ping}
223-
referrerPolicy={referrerPolicy}
224-
isDisabled={isDisabled || isCurrent}
225-
className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}>
192+
className={({isCurrent}) => breadcrumbStyles({size, isCurrent})}>
193+
{({isCurrent}) => (
194+
isCurrent ?
195+
<span
196+
className={currentStyles({size})}>
197+
<Provider
198+
values={[
199+
[HeadingContext, {className: heading}]
200+
]}>
226201
{children}
227-
</Link>
228-
<ChevronIcon
229-
size="M"
230-
className={chevronStyles} />
231-
</>
202+
</Provider>
203+
</span>
204+
: (
205+
<>
206+
<Link
207+
style={({isFocusVisible}) => ({clipPath: isFocusVisible ? 'none' : 'margin-box'})}
208+
href={href}
209+
target={target}
210+
rel={rel}
211+
download={download}
212+
ping={ping}
213+
referrerPolicy={referrerPolicy}
214+
isDisabled={isDisabled || isCurrent}
215+
className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}>
216+
{children}
217+
</Link>
218+
<ChevronIcon
219+
size="M"
220+
className={chevronStyles} />
221+
</>
222+
)
232223
)}
233224
</AriaBreadcrumb>
234225
);

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
*/
1212

1313
import {baseColor, fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'};
14-
import {ButtonRenderProps, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components';
14+
import {ButtonRenderProps, ContextValue, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components';
1515
import {centerBaseline} from './CenterBaseline';
1616
import {centerPadding, focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
1717
import {createContext, forwardRef, ReactNode, useContext} from 'react';
18-
import {FocusableRef} from '@react-types/shared';
18+
import {FocusableRef, FocusableRefValue} from '@react-types/shared';
1919
import {IconContext} from './Icon';
20-
import {mergeProps} from 'react-aria';
2120
import {pressScale} from './pressScale';
2221
import {Text, TextContext} from './Content';
2322
import {useFocusableRef} from '@react-spectrum/utils';
23+
import {useSpectrumContextProps} from './useSpectrumContextProps';
2424

2525
interface ButtonStyleProps {
2626
/**
@@ -55,12 +55,8 @@ export interface LinkButtonProps extends Omit<LinkProps, 'className' | 'style' |
5555
children?: ReactNode
5656
}
5757

58-
interface ButtonContextValue extends ButtonStyleProps, StyleProps {
59-
/** Whether the Button is disabled. */
60-
isDisabled?: boolean
61-
}
62-
63-
export const ButtonContext = createContext<ButtonContextValue>({});
58+
export const ButtonContext = createContext<ContextValue<ButtonProps, FocusableRefValue<HTMLButtonElement>>>(null);
59+
export const LinkButtonContext = createContext<ContextValue<ButtonProps, FocusableRefValue<HTMLAnchorElement>>>(null);
6460

6561
const button = style<ButtonRenderProps & ButtonStyleProps>({
6662
...focusRing(),
@@ -275,9 +271,8 @@ const button = style<ButtonRenderProps & ButtonStyleProps>({
275271
}, getAllowedOverrides());
276272

277273
function Button(props: ButtonProps, ref: FocusableRef<HTMLButtonElement>) {
274+
[props, ref] = useSpectrumContextProps(props, ref, ButtonContext);
278275
let domRef = useFocusableRef(ref);
279-
let ctx = useContext(ButtonContext);
280-
props = mergeProps(ctx, props);
281276
let overlayTriggerState = useContext(OverlayTriggerStateContext);
282277

283278
return (
@@ -296,9 +291,9 @@ function Button(props: ButtonProps, ref: FocusableRef<HTMLButtonElement>) {
296291
}, props.styles)}>
297292
<Provider
298293
values={[
299-
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
294+
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
300295
[IconContext, {
301-
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
296+
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
302297
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
303298
}]
304299
]}>
@@ -317,9 +312,8 @@ let _Button = forwardRef(Button);
317312
export {_Button as Button};
318313

319314
function LinkButton(props: LinkButtonProps, ref: FocusableRef<HTMLAnchorElement>) {
315+
[props, ref] = useSpectrumContextProps(props, ref, LinkButtonContext);
320316
let domRef = useFocusableRef(ref);
321-
let ctx = useContext(ButtonContext);
322-
props = mergeProps(ctx, props);
323317
let overlayTriggerState = useContext(OverlayTriggerStateContext);
324318

325319
return (
@@ -338,9 +332,9 @@ function LinkButton(props: LinkButtonProps, ref: FocusableRef<HTMLAnchorElement>
338332
}, props.styles)}>
339333
<Provider
340334
values={[
341-
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
335+
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
342336
[IconContext, {
343-
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
337+
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
344338
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
345339
}]
346340
]}>

0 commit comments

Comments
 (0)