From f30983a09257d8bc3140bdbd3e917a24e09fde78 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Wed, 14 Aug 2024 09:13:25 -0700 Subject: [PATCH] Add slot contexts to all S2 components (#6856) --- .../@react-spectrum/s2/src/ActionButton.tsx | 14 +- .../@react-spectrum/s2/src/ActionMenu.tsx | 9 +- packages/@react-spectrum/s2/src/Avatar.tsx | 9 +- packages/@react-spectrum/s2/src/Badge.tsx | 16 ++- .../@react-spectrum/s2/src/Breadcrumbs.tsx | 101 ++++++------- packages/@react-spectrum/s2/src/Button.tsx | 28 ++-- .../@react-spectrum/s2/src/ButtonGroup.tsx | 23 +-- .../@react-spectrum/s2/src/CenterBaseline.tsx | 15 +- packages/@react-spectrum/s2/src/Checkbox.tsx | 19 +-- .../@react-spectrum/s2/src/CheckboxGroup.tsx | 11 +- packages/@react-spectrum/s2/src/ColorArea.tsx | 11 +- .../@react-spectrum/s2/src/ColorField.tsx | 9 +- .../@react-spectrum/s2/src/ColorSlider.tsx | 9 +- .../@react-spectrum/s2/src/ColorSwatch.tsx | 15 +- .../s2/src/ColorSwatchPicker.tsx | 18 ++- .../@react-spectrum/s2/src/ColorWheel.tsx | 11 +- packages/@react-spectrum/s2/src/ComboBox.tsx | 28 ++-- packages/@react-spectrum/s2/src/Content.tsx | 133 +++++++++++++----- .../@react-spectrum/s2/src/ContextualHelp.tsx | 19 +-- packages/@react-spectrum/s2/src/Dialog.tsx | 48 +++---- packages/@react-spectrum/s2/src/Divider.tsx | 10 +- packages/@react-spectrum/s2/src/DropZone.tsx | 10 +- packages/@react-spectrum/s2/src/Field.tsx | 4 +- .../s2/src/IllustratedMessage.tsx | 27 ++-- .../@react-spectrum/s2/src/InlineAlert.tsx | 16 ++- packages/@react-spectrum/s2/src/Link.tsx | 16 ++- packages/@react-spectrum/s2/src/Menu.tsx | 31 ++-- packages/@react-spectrum/s2/src/Meter.tsx | 11 +- .../@react-spectrum/s2/src/NumberField.tsx | 16 ++- packages/@react-spectrum/s2/src/Picker.tsx | 27 ++-- .../@react-spectrum/s2/src/ProgressBar.tsx | 11 +- .../@react-spectrum/s2/src/ProgressCircle.tsx | 11 +- packages/@react-spectrum/s2/src/Radio.tsx | 4 +- .../@react-spectrum/s2/src/RadioGroup.tsx | 11 +- .../@react-spectrum/s2/src/RangeSlider.tsx | 15 +- .../@react-spectrum/s2/src/SearchField.tsx | 9 +- packages/@react-spectrum/s2/src/Slider.tsx | 9 +- .../@react-spectrum/s2/src/StatusLight.tsx | 11 +- packages/@react-spectrum/s2/src/Switch.tsx | 13 +- packages/@react-spectrum/s2/src/TagGroup.tsx | 18 ++- packages/@react-spectrum/s2/src/TextField.tsx | 18 ++- .../@react-spectrum/s2/src/ToggleButton.tsx | 14 +- packages/@react-spectrum/s2/src/index.ts | 76 +++++----- .../s2/src/useSpectrumContextProps.ts | 45 ++++++ packages/@react-spectrum/s2/style/runtime.ts | 15 +- .../textfield/src/TextArea.tsx | 2 +- .../textfield/src/TextFieldBase.tsx | 2 +- .../@react-types/textfield/src/index.d.ts | 4 +- .../react-aria-components/src/Breadcrumbs.tsx | 6 +- .../react-aria-components/src/Checkbox.tsx | 6 +- .../react-aria-components/src/RadioGroup.tsx | 6 +- packages/react-aria-components/src/Switch.tsx | 6 +- packages/react-aria-components/src/utils.tsx | 2 +- 53 files changed, 636 insertions(+), 392 deletions(-) create mode 100644 packages/@react-spectrum/s2/src/useSpectrumContextProps.ts diff --git a/packages/@react-spectrum/s2/src/ActionButton.tsx b/packages/@react-spectrum/s2/src/ActionButton.tsx index 1d168660a2d..81acfaa697c 100644 --- a/packages/@react-spectrum/s2/src/ActionButton.tsx +++ b/packages/@react-spectrum/s2/src/ActionButton.tsx @@ -11,15 +11,16 @@ */ import {baseColor, fontRelative, style} from '../style/spectrum-theme' with { type: 'macro' }; -import {ButtonProps, ButtonRenderProps, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components'; +import {ButtonProps, ButtonRenderProps, ContextValue, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components'; import {centerBaseline} from './CenterBaseline'; -import {FocusableRef} from '@react-types/shared'; +import {createContext, forwardRef, ReactNode, useContext} from 'react'; +import {FocusableRef, FocusableRefValue} from '@react-types/shared'; import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with { type: 'macro' }; -import {forwardRef, ReactNode, useContext} from 'react'; import {IconContext} from './Icon'; import {pressScale} from './pressScale'; import {TextContext} from './Content'; import {useFocusableRef} from '@react-spectrum/utils'; +import {useSpectrumContextProps} from './useSpectrumContextProps'; export interface ActionButtonStyleProps { /** @@ -170,7 +171,10 @@ export const btnStyles = style>>(null); + function ActionButton(props: ActionButtonProps, ref: FocusableRef) { + [props, ref] = useSpectrumContextProps(props, ref, ActionButtonContext); let domRef = useFocusableRef(ref); let overlayTriggerState = useContext(OverlayTriggerStateContext); @@ -189,9 +193,9 @@ function ActionButton(props: ActionButtonProps, ref: FocusableRef diff --git a/packages/@react-spectrum/s2/src/ActionMenu.tsx b/packages/@react-spectrum/s2/src/ActionMenu.tsx index 0bbec20b1ad..169ec639b3a 100644 --- a/packages/@react-spectrum/s2/src/ActionMenu.tsx +++ b/packages/@react-spectrum/s2/src/ActionMenu.tsx @@ -11,13 +11,15 @@ */ import {ActionButton, ActionButtonProps} from './ActionButton'; -import {AriaLabelingProps, DOMProps, FocusableRef} from '@react-types/shared'; +import {AriaLabelingProps, DOMProps, FocusableRef, FocusableRefValue} from '@react-types/shared'; +import {ContextValue} from 'react-aria-components'; +import {createContext, forwardRef} from 'react'; import {filterDOMProps} from '@react-aria/utils'; -import {forwardRef} from 'react'; import {forwardRefType} from './types'; import {Menu, MenuProps, MenuTrigger, MenuTriggerProps} from './Menu'; import MoreIcon from '../s2wf-icons/S2_Icon_More_20_N.svg'; import {StyleProps} from './style-utils' with { type: 'macro' }; +import {useSpectrumContextProps} from './useSpectrumContextProps'; export interface ActionMenuProps extends Pick, @@ -26,7 +28,10 @@ export interface ActionMenuProps extends StyleProps, DOMProps, AriaLabelingProps { } +export const ActionMenuContext = createContext, FocusableRefValue>>(null); + function ActionMenu(props: ActionMenuProps, ref: FocusableRef) { + [props, ref] = useSpectrumContextProps(props, ref, ActionMenuContext); let buttonProps = filterDOMProps(props, {labelable: true}); // size independently controlled? diff --git a/packages/@react-spectrum/s2/src/Avatar.tsx b/packages/@react-spectrum/s2/src/Avatar.tsx index c4ae485e038..369b9ef0e5f 100644 --- a/packages/@react-spectrum/s2/src/Avatar.tsx +++ b/packages/@react-spectrum/s2/src/Avatar.tsx @@ -10,13 +10,14 @@ * governing permissions and limitations under the License. */ -import {ContextValue, useContextProps} from 'react-aria-components'; +import {ContextValue} from 'react-aria-components'; import {createContext, forwardRef} from 'react'; -import {DOMProps, DOMRef} from '@react-types/shared'; +import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; import {getAllowedOverrides, StyleProps, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'}; import {style} from '../style/spectrum-theme' with { type: 'macro' }; import {useDOMRef} from '@react-spectrum/utils'; +import {useSpectrumContextProps} from './useSpectrumContextProps'; export interface AvatarProps extends StyleProps, DOMProps { /** Text description of the avatar. */ @@ -40,11 +41,11 @@ const imageStyles = style({ disableTapHighlight: true }, getAllowedOverrides({height: true})); -export const AvatarContext = createContext>({}); +export const AvatarContext = createContext>>(null); function Avatar(props: AvatarProps, ref: DOMRef) { + [props, ref] = useSpectrumContextProps(props, ref, AvatarContext); let domRef = useDOMRef(ref); - [props, domRef] = useContextProps(props, domRef, AvatarContext); let { alt = '', src, diff --git a/packages/@react-spectrum/s2/src/Badge.tsx b/packages/@react-spectrum/s2/src/Badge.tsx index 9bd534811d0..4c9fda7473f 100644 --- a/packages/@react-spectrum/s2/src/Badge.tsx +++ b/packages/@react-spectrum/s2/src/Badge.tsx @@ -10,16 +10,17 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, DOMProps, DOMRef} from '@react-types/shared'; +import {AriaLabelingProps, DOMProps, DOMRef, DOMRefValue} from '@react-types/shared'; import {centerBaseline} from './CenterBaseline'; import {centerPadding, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; +import {ContextValue, Provider, SlotProps} from 'react-aria-components'; import {filterDOMProps} from '@react-aria/utils'; import {fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'}; import {IconContext} from './Icon'; -import {Provider} from 'react-aria-components'; -import React, {forwardRef, ReactNode} from 'react'; +import React, {createContext, forwardRef, ReactNode} from 'react'; import {Text, TextContext} from './Content'; import {useDOMRef} from '@react-spectrum/utils'; +import {useSpectrumContextProps} from './useSpectrumContextProps'; export interface BadgeStyleProps { /** @@ -36,13 +37,15 @@ export interface BadgeStyleProps { 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' } -export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps{ +export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps, SlotProps { /** * The content to display in the badge. */ children: ReactNode } +export const BadgeContext = createContext, DOMRefValue>>(null); + const badge = style({ display: 'flex', font: 'control', @@ -115,6 +118,7 @@ const badge = style({ }, getAllowedOverrides()); function Badge(props: BadgeProps, ref: DOMRef) { + [props, ref] = useSpectrumContextProps(props, ref, BadgeContext); let { children, variant = 'neutral', @@ -127,9 +131,9 @@ function Badge(props: BadgeProps, ref: DOMRef) { return ( diff --git a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx index 44e96870c1f..2e3a7c76b2f 100644 --- a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx +++ b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx @@ -10,13 +10,13 @@ * governing permissions and limitations under the License. */ -import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs} from 'react-aria-components'; +import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, ContextValue, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs, useSlottedContext} from 'react-aria-components'; import {AriaBreadcrumbItemProps} from 'react-aria'; import ChevronIcon from '../ui-icons/Chevron'; -import {Children, cloneElement, createContext, forwardRef, isValidElement, ReactElement, ReactNode, useContext, useRef} from 'react'; +import {createContext, forwardRef, ReactNode, useRef} from 'react'; +import {DOMRefValue, LinkDOMProps} from '@react-types/shared'; import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {forwardRefType} from './types'; -import {LinkDOMProps} from '@react-types/shared'; import {size, style} from '../style/spectrum-theme' with { type: 'macro' }; interface BreadcrumbsStyleProps { @@ -41,6 +41,8 @@ export interface BreadcrumbsProps extends Omit, 'chil children?: ReactNode } +export const BreadcrumbsContext = createContext, DOMRefValue>>(null); + const wrapper = style({ display: 'flex', justifyContent: 'start', @@ -67,38 +69,27 @@ const wrapper = style({ } }, getAllowedOverrides()); -const BreadcrumbsInternalContext = createContext & {length: number}>({length: 0}); - -function Breadcrumbs({ +function Breadcrumbs(props: BreadcrumbsProps) { + let { UNSAFE_className = '', UNSAFE_style, styles, - ...props -}: BreadcrumbsProps) { - let {size = 'M', isDisabled} = props; + size = 'M', + children, + ...otherProps + } = props; let ref = useRef(null); - // TODO: Remove when https://github.com/adobe/react-spectrum/pull/6440 is released - let childArray: ReactElement[] = []; - Children.forEach(props.children, (child, index) => { - if (isValidElement<{index: number}>(child)) { - child = cloneElement(child, {key: index, index}); - childArray.push(child); - } - }); return ( - - {childArray} - + + {children} + ); } @@ -192,43 +183,43 @@ export interface BreadcrumbProps extends Omit - {isCurrent ? - - - {children} - - - : ( - <> - ({clipPath: isFocusVisible ? 'none' : 'margin-box'})} - href={href} - target={target} - rel={rel} - download={download} - ping={ping} - referrerPolicy={referrerPolicy} - isDisabled={isDisabled || isCurrent} - className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}> + className={({isCurrent}) => breadcrumbStyles({size, isCurrent})}> + {({isCurrent}) => ( + isCurrent ? + + {children} - - - + + + : ( + <> + ({clipPath: isFocusVisible ? 'none' : 'margin-box'})} + href={href} + target={target} + rel={rel} + download={download} + ping={ping} + referrerPolicy={referrerPolicy} + isDisabled={isDisabled || isCurrent} + className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}> + {children} + + + + ) )} ); diff --git a/packages/@react-spectrum/s2/src/Button.tsx b/packages/@react-spectrum/s2/src/Button.tsx index 87118e06e8b..fa29d9e14d8 100644 --- a/packages/@react-spectrum/s2/src/Button.tsx +++ b/packages/@react-spectrum/s2/src/Button.tsx @@ -11,16 +11,16 @@ */ import {baseColor, fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'}; -import {ButtonRenderProps, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components'; +import {ButtonRenderProps, ContextValue, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components'; import {centerBaseline} from './CenterBaseline'; import {centerPadding, focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {createContext, forwardRef, ReactNode, useContext} from 'react'; -import {FocusableRef} from '@react-types/shared'; +import {FocusableRef, FocusableRefValue} from '@react-types/shared'; import {IconContext} from './Icon'; -import {mergeProps} from 'react-aria'; import {pressScale} from './pressScale'; import {Text, TextContext} from './Content'; import {useFocusableRef} from '@react-spectrum/utils'; +import {useSpectrumContextProps} from './useSpectrumContextProps'; interface ButtonStyleProps { /** @@ -55,12 +55,8 @@ export interface LinkButtonProps extends Omit({}); +export const ButtonContext = createContext>>(null); +export const LinkButtonContext = createContext>>(null); const button = style({ ...focusRing(), @@ -275,9 +271,8 @@ const button = style({ }, getAllowedOverrides()); function Button(props: ButtonProps, ref: FocusableRef) { + [props, ref] = useSpectrumContextProps(props, ref, ButtonContext); let domRef = useFocusableRef(ref); - let ctx = useContext(ButtonContext); - props = mergeProps(ctx, props); let overlayTriggerState = useContext(OverlayTriggerStateContext); return ( @@ -296,9 +291,9 @@ function Button(props: ButtonProps, ref: FocusableRef) { }, props.styles)}> @@ -317,9 +312,8 @@ let _Button = forwardRef(Button); export {_Button as Button}; function LinkButton(props: LinkButtonProps, ref: FocusableRef) { + [props, ref] = useSpectrumContextProps(props, ref, LinkButtonContext); let domRef = useFocusableRef(ref); - let ctx = useContext(ButtonContext); - props = mergeProps(ctx, props); let overlayTriggerState = useContext(OverlayTriggerStateContext); return ( @@ -338,9 +332,9 @@ function LinkButton(props: LinkButtonProps, ref: FocusableRef }, props.styles)}> diff --git a/packages/@react-spectrum/s2/src/ButtonGroup.tsx b/packages/@react-spectrum/s2/src/ButtonGroup.tsx index 10acf7c9e4a..e30416a27fe 100644 --- a/packages/@react-spectrum/s2/src/ButtonGroup.tsx +++ b/packages/@react-spectrum/s2/src/ButtonGroup.tsx @@ -10,18 +10,18 @@ * governing permissions and limitations under the License. */ -import {ButtonContext} from './Button'; -import {ContextValue} from './Content'; +import {ButtonContext, LinkButtonContext} from './Button'; +import {ContextValue, Provider, SlotProps} from 'react-aria-components'; import {createContext, forwardRef, ReactNode, useCallback, useRef} from 'react'; -import {DOMProps, DOMRef} from '@react-types/shared'; +import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared'; import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; -import {Provider, SlotProps, useContextProps} from 'react-aria-components'; import {style} from '../style/spectrum-theme' with {type: 'macro'}; import { useDOMRef, useResizeObserver } from '@react-spectrum/utils'; import {useLayoutEffect, useValueEffect} from '@react-aria/utils'; +import {useSpectrumContextProps} from './useSpectrumContextProps'; interface ButtonGroupStyleProps { /** @@ -52,12 +52,12 @@ export interface ButtonGroupProps extends ButtonGroupStyleProps, SlotProps, Styl isDisabled?: boolean } -interface ButtonGroupContextValue extends Omit { +interface ButtonGroupContextValue extends Partial { /** Whether the ButtonGroup shouldn't be rendered. */ - hidden?: boolean + isHidden?: boolean } -export const ButtonGroupContext = createContext>({}); +export const ButtonGroupContext = createContext>>({}); const buttongroup = style({ display: 'inline-flex', @@ -102,8 +102,8 @@ const buttongroup = style({ }, getAllowedOverrides()); function ButtonGroup(props: ButtonGroupProps, ref: DOMRef) { + [props, ref] = useSpectrumContextProps(props, ref, ButtonGroupContext); let domRef = useDOMRef(ref); - [props, domRef] = useContextProps(props, domRef, ButtonGroupContext); let { size = 'M', orientation = 'horizontal', @@ -155,9 +155,11 @@ function ButtonGroup(props: ButtonGroupProps, ref: DOMRef) { }, [domRef.current]); useResizeObserver({ref: parent, onResize: checkForOverflow}); - if ((props as ButtonGroupContextValue).hidden) { + if ((props as ButtonGroupContextValue).isHidden) { return null; } + + let context = {styles: style({flexShrink: 0}), size, isDisabled}; return (
) { }, props.styles)}> {children} diff --git a/packages/@react-spectrum/s2/src/CenterBaseline.tsx b/packages/@react-spectrum/s2/src/CenterBaseline.tsx index 7fe07fcacc4..7a5e78e1a8a 100644 --- a/packages/@react-spectrum/s2/src/CenterBaseline.tsx +++ b/packages/@react-spectrum/s2/src/CenterBaseline.tsx @@ -11,18 +11,29 @@ */ import {CSSProperties, ReactNode} from 'react'; +import {mergeStyles} from '../style/runtime'; import {raw} from '../style/style-macro' with {type: 'macro'}; +import {style} from '../style/spectrum-theme' with {type: 'macro'}; +import {StyleString} from '../style/types'; interface CenterBaselineProps { style?: CSSProperties, - className?: string, + styles?: StyleString, children: ReactNode, slot?: string } +const styles = style({ + display: 'flex', + alignItems: 'center' +}); + export function CenterBaseline(props: CenterBaselineProps) { return ( -