diff --git a/packages/@react-spectrum/s2/src/CheckboxGroup.tsx b/packages/@react-spectrum/s2/src/CheckboxGroup.tsx index 6ca087263d1..693a18508e4 100644 --- a/packages/@react-spectrum/s2/src/CheckboxGroup.tsx +++ b/packages/@react-spectrum/s2/src/CheckboxGroup.tsx @@ -23,7 +23,7 @@ import {StyleProps, field, getAllowedOverrides} from './style-utils' with {type: import {useDOMRef} from '@react-spectrum/utils'; import {CheckboxContext} from './Checkbox'; -export interface CheckboxGroupProps extends Omit, StyleProps, Omit, HelpTextProps { +export interface CheckboxGroupProps extends Omit, StyleProps, SpectrumLabelableProps, HelpTextProps { /** * The size of the Checkboxes in the CheckboxGroup. * @@ -91,7 +91,8 @@ function CheckboxGroup(props: CheckboxGroupProps, ref: DOMRef) { size={size} labelPosition={labelPosition} labelAlign={labelAlign} - necessityIndicator={necessityIndicator}> + necessityIndicator={necessityIndicator} + contextualHelp={props.contextualHelp}> {label}
, StyleProps, Omit, HelpTextProps { +export interface ColorFieldProps extends Omit, StyleProps, SpectrumLabelableProps, HelpTextProps { /** * The size of the color field. * @@ -81,7 +81,8 @@ function ColorField(props: ColorFieldProps, ref: Ref) { size={props.size} labelPosition={labelPosition} labelAlign={labelAlign} - necessityIndicator={necessityIndicator}> + necessityIndicator={necessityIndicator} + contextualHelp={props.contextualHelp}> {label} diff --git a/packages/@react-spectrum/s2/src/ColorHandle.tsx b/packages/@react-spectrum/s2/src/ColorHandle.tsx index 50953929439..f9e1fdab605 100644 --- a/packages/@react-spectrum/s2/src/ColorHandle.tsx +++ b/packages/@react-spectrum/s2/src/ColorHandle.tsx @@ -12,8 +12,8 @@ import {ColorThumb} from 'react-aria-components'; import {style} from '../style/spectrum-theme' with {type: 'macro'}; -import {RefObject, cloneElement, useId, useState} from 'react'; -import {useLayoutEffect} from '@react-aria/utils'; +import {RefObject, cloneElement, useState} from 'react'; +import {useId, useLayoutEffect} from '@react-aria/utils'; import {createPortal} from 'react-dom'; import {keyframes} from '../style/style-macro' with {type: 'macro'}; diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 1112aa2a81e..85cf35e3eff 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -68,7 +68,7 @@ export interface ComboBoxProps extends Omit, 'children' | 'style' | 'className' | 'defaultFilter' | 'allowsEmptyCollection'>, ComboboxStyleProps, StyleProps, - Omit, + SpectrumLabelableProps, HelpTextProps, Pick, 'items'>, Pick { @@ -217,7 +217,8 @@ function ComboBox(props: ComboBoxProps, ref: FocusableRef + necessityIndicator={necessityIndicator} + contextualHelp={props.contextualHelp}> {label} , + Pick, + Pick, + ContextualHelpStyleProps, StyleProps, DOMProps, AriaLabelingProps { + /** Contents of the Contextual Help popover. */ + children?: ReactNode +} + +const popover = style({ + fontFamily: 'sans', + minWidth: '[218px]', + width: '[218px]', + padding: 24 +}); + +export const ContextualHelpContext = createContext>({}); + +function ContextualHelp(props: ContextualHelpProps, ref: FocusableRef) { + let domRef = useFocusableRef(ref); + [props, domRef] = useContextProps(props, domRef, ContextualHelpContext); + let { + children, + defaultOpen, + // containerPadding = 24, // See popover() above. Issue noted in Popover.tsx. + size = 'XS', + crossOffset, + isOpen, + offset = 8, + onOpenChange, + placement = 'bottom start', + shouldFlip, + UNSAFE_className, + UNSAFE_style, + variant = 'help' + } = props; + + // In a FieldLabel we're getting the context's aria-labeledby, so we need to + // manually set the aria-label after useLabels() to keep the order of label + // then ContextualHelp variant + let labelProps = useLabels(props); + // Translate variant + labelProps['aria-label'] = labelProps['aria-label'] ? labelProps['aria-label'] + ' ' + variant : variant; + + let buttonProps = filterDOMProps(props, {labelable: true}); + + return ( + + + {variant === 'info' ? : } + + containerPadding={containerPadding} + offset={offset} + crossOffset={crossOffset} + hideArrow + UNSAFE_className={popover}> + + + {children} + + + + + ); +} + +/** + * Contextual help shows a user extra information about the state of an adjacent component, or a total view. + */ +let _ContextualHelp = forwardRef(ContextualHelp); +export {_ContextualHelp as ContextualHelp}; diff --git a/packages/@react-spectrum/s2/src/Dialog.tsx b/packages/@react-spectrum/s2/src/Dialog.tsx index 090df943d3c..1fb00f300be 100644 --- a/packages/@react-spectrum/s2/src/Dialog.tsx +++ b/packages/@react-spectrum/s2/src/Dialog.tsx @@ -154,7 +154,7 @@ function Dialog(props: DialogProps, ref: DOMRef) { let _Dialog = forwardRef(Dialog); export {_Dialog as Dialog}; -const dialogInner = style({ +export const dialogInner = style({ display: 'flex', flexDirection: 'column', flexGrow: 1, diff --git a/packages/@react-spectrum/s2/src/Field.tsx b/packages/@react-spectrum/s2/src/Field.tsx index f231247b290..4f27696533c 100644 --- a/packages/@react-spectrum/s2/src/Field.tsx +++ b/packages/@react-spectrum/s2/src/Field.tsx @@ -12,6 +12,7 @@ import {Group, GroupProps, Input as RACInput, InputProps as RACInputProps, Label, LabelProps, FieldErrorProps, FieldError, composeRenderProps, Text, Provider} from 'react-aria-components'; import {baseColor, fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'}; +import {ContextualHelpContext} from './ContextualHelp'; import {StyleProps, UnsafeStyles, fieldInput, focusRing} from './style-utils' with {type: 'macro'}; import AsteriskIcon from '../ui-icons/Asterisk'; import AlertIcon from '../s2wf-icons/assets/svg/S2_Icon_AlertTriangle_20_N.svg'; @@ -21,6 +22,7 @@ import {CenterBaseline, centerBaseline, centerBaselineBefore} from './CenterBase import {NecessityIndicator, Alignment, DOMRef} from '@react-types/shared'; import {forwardRef, ForwardedRef, ReactNode} from 'react'; import {useDOMRef} from '@react-spectrum/utils'; +import {useId} from '@react-aria/utils'; import {StyleString} from '../style/types'; interface FieldLabelProps extends Omit, StyleProps { @@ -32,6 +34,7 @@ interface FieldLabelProps extends Omit) { labelAlign, labelPosition, staticColor, + contextualHelp, UNSAFE_style, UNSAFE_className = '', ...labelProps } = props; let domRef = useDOMRef(ref); + let contextualHelpId = useId(); + let fallbackLabelPropsId = useId(); + if (contextualHelp && !labelProps.id) { + labelProps.id = fallbackLabelPropsId; + } return ( - +
); } diff --git a/packages/@react-spectrum/s2/src/Picker.tsx b/packages/@react-spectrum/s2/src/Picker.tsx index 2c5c4414a28..1edc325f203 100644 --- a/packages/@react-spectrum/s2/src/Picker.tsx +++ b/packages/@react-spectrum/s2/src/Picker.tsx @@ -74,7 +74,7 @@ export interface PickerProps extends Omit, 'children' | 'style' | 'className'>, PickerStyleProps, StyleProps, - Omit, + SpectrumLabelableProps, HelpTextProps, Pick, 'items'>, Pick { @@ -230,7 +230,8 @@ function Picker(props: PickerProps, ref: FocusableRef + necessityIndicator={necessityIndicator} + contextualHelp={props.contextualHelp}> {label}