feat: Add support for multi-select ComboBox#9525
Conversation
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Super excited for this! |
…ct-combobox # Conflicts: # packages/@react-spectrum/s2/src/ComboBox.tsx
|
Build successful! 🎉 |
| }); | ||
|
|
||
| // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically. | ||
| // TODO: do we need to do this for multi-select? |
There was a problem hiding this comment.
Right now I just hear "51 options available", which interrupts the default selection announcement from VO. But I also don't hear this with single select. We should discuss whether we can just remove the custom announcements entirely at this point.
There was a problem hiding this comment.
That makes sense, I think ComboBox is overdue for an accessibility sweep anyways since there have been reported issues against Talkback, the whole autocomplete announcement issue with auto focusing the first option on focus, etc
|
Build successful! 🎉 |
|
Build successful! 🎉 |
LFDanLu
left a comment
There was a problem hiding this comment.
initial review as I digest the combobox state changes
| }); | ||
|
|
||
| // Announce when a selection occurs for VoiceOver. Other screen readers typically do this automatically. | ||
| // TODO: do we need to do this for multi-select? |
There was a problem hiding this comment.
That makes sense, I think ComboBox is overdue for an accessibility sweep anyways since there have been reported issues against Talkback, the whole autocomplete announcement issue with auto focusing the first option on focus, etc
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
## API Changes
react-aria-components/react-aria-components:ComboBox-ComboBox <T extends {}> {
+ComboBox <M extends SelectionMode = 'single', T extends {}> {
allowsCustomValue?: boolean
allowsEmptyCollection?: boolean
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ChildrenOrFunction<ComboBoxRenderProps>
className?: ClassNameOrFunction<ComboBoxRenderProps> = 'react-aria-ComboBox'
defaultFilter?: (string, string) => boolean
defaultInputValue?: string
defaultItems?: Iterable<T>
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
disabledKeys?: Iterable<Key>
form?: string
formValue?: 'text' | 'key' = 'key'
id?: string
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
menuTrigger?: MenuTriggerAction = 'input'
name?: string
onBlur?: (FocusEvent<HTMLInputElement>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<HTMLInputElement>) => void
onFocusChange?: (boolean) => void
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
- onSelectionChange?: (Key | null) => void
render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, ComboBoxRenderProps>
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFocusWrap?: boolean
slot?: string | null
style?: StyleOrFunction<ComboBoxRenderProps>
validate?: (ComboBoxValidationValue) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
}/react-aria-components:Provider-Provider <A, B, C, D, E, F, G, H, I, J, K> {
+Provider <A, B, C, D, E, F, G, H, I, J, K, L> {
children: ReactNode
- values: ProviderValues<A, B, C, D, E, F, G, H, I, J, K>
+ values: ProviderValues<A, B, C, D, E, F, G, H, I, J, K, L>
}/react-aria-components:ComboBoxProps-ComboBoxProps <T extends {}> {
+ComboBoxProps <M extends SelectionMode = 'single', T extends {}> {
allowsCustomValue?: boolean
allowsEmptyCollection?: boolean
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ChildrenOrFunction<ComboBoxRenderProps>
className?: ClassNameOrFunction<ComboBoxRenderProps> = 'react-aria-ComboBox'
defaultFilter?: (string, string) => boolean
defaultInputValue?: string
defaultItems?: Iterable<T>
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
disabledKeys?: Iterable<Key>
form?: string
formValue?: 'text' | 'key' = 'key'
id?: string
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
menuTrigger?: MenuTriggerAction = 'input'
name?: string
onBlur?: (FocusEvent<HTMLInputElement>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<HTMLInputElement>) => void
onFocusChange?: (boolean) => void
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
- onSelectionChange?: (Key | null) => void
render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, ComboBoxRenderProps>
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFocusWrap?: boolean
slot?: string | null
style?: StyleOrFunction<ComboBoxRenderProps>
validate?: (ComboBoxValidationValue) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
}/react-aria-components:ComboBoxState-ComboBoxState <T> {
+ComboBoxState <M extends SelectionMode = 'single', T> {
close: () => void
collection: Collection<Node<T>>
commit: () => void
commitValidation: () => void
defaultInputValue: string
- defaultSelectedKey: Key | null
+ defaultValue: ValueType<SelectionMode>
disabledKeys: Set<Key>
displayValidation: ValidationResult
focusStrategy: FocusStrategy | null
inputValue: string
isFocused: boolean
isOpen: boolean
open: (FocusStrategy | null, MenuTriggerAction) => void
realtimeValidation: ValidationResult
resetValidation: () => void
revert: () => void
- selectedItem: Node<T> | null
- selectedKey: Key | null
+ selectedItems: Array<Node<T>>
selectionManager: SelectionManager
setFocused: (boolean) => void
setInputValue: (string) => void
setOpen: (boolean) => void
- setSelectedKey: (Key | null) => void
+ setValue: (Key | readonly Array<Key> | null) => void
toggle: (FocusStrategy | null, MenuTriggerAction) => void
updateValidation: (ValidationResult) => void
+ value: ValueType<SelectionMode>
}/react-aria-components:ComboBoxValue+ComboBoxValue <T extends {}> extends HTMLAttributes {
+ children?: ChildrenOrFunction<ComboBoxValueRenderProps<{}>>
+ className?: ClassNameOrFunction<ComboBoxValueRenderProps<{}>> = 'react-aria-ComboBoxValue'
+ placeholder?: ReactNode
+ render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, ComboBoxValueRenderProps<{}>>
+ style?: StyleOrFunction<ComboBoxValueRenderProps<{}>>
+}/react-aria-components:ComboBoxValueContext+ComboBoxValueContext {
+ UNTYPED
+}/react-aria-components:ComboBoxValueProps+ComboBoxValueProps <T extends {}> {
+ children?: ChildrenOrFunction<ComboBoxValueRenderProps<{}>>
+ className?: ClassNameOrFunction<ComboBoxValueRenderProps<{}>> = 'react-aria-ComboBoxValue'
+ placeholder?: ReactNode
+ render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, ComboBoxValueRenderProps<{}>>
+ style?: StyleOrFunction<ComboBoxValueRenderProps<{}>>
+}/react-aria-components:ComboBoxValueRenderProps+ComboBoxValueRenderProps <T> {
+ isPlaceholder: boolean
+ selectedItems: Array<T | null>
+ selectedText: string
+ state: ComboBoxState<T, 'single' | 'multiple'>
+}@react-aria/autocomplete/@react-aria/autocomplete:AriaSearchAutocompleteOptions AriaSearchAutocompleteOptions <T> {
aria-activedescendant?: string
aria-autocomplete?: 'none' | 'inline' | 'list' | 'both'
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-errormessage?: string
aria-haspopup?: boolean | 'false' | 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoCorrect?: string
autoFocus?: boolean
children: CollectionChildren<T>
defaultInputValue?: string
defaultItems?: Iterable<T>
description?: ReactNode
disabledKeys?: Iterable<Key>
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
inputRef: RefObject<HTMLInputElement | null>
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
keyboardDelegate?: KeyboardDelegate
label?: ReactNode
layoutDelegate?: LayoutDelegate
listBoxRef: RefObject<HTMLElement | null>
maxLength?: number
menuTrigger?: MenuTriggerAction = 'input'
minLength?: number
name?: string
onBeforeInput?: FormEventHandler<HTMLInputElement>
onBlur?: (FocusEvent<T>) => void
- onChange?: (T) => void
onClear?: () => void
onCompositionEnd?: CompositionEventHandler<HTMLInputElement>
onCompositionStart?: CompositionEventHandler<HTMLInputElement>
onCompositionUpdate?: CompositionEventHandler<HTMLInputElement>
onCut?: ClipboardEventHandler<HTMLInputElement>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<HTMLInputElement>
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
onPaste?: ClipboardEventHandler<HTMLInputElement>
onSelect?: ReactEventHandler<HTMLInputElement>
onSubmit?: (string | null, Key | null) => void
pattern?: string
placeholder?: string
popoverRef: RefObject<HTMLDivElement | null>
spellCheck?: string
type?: 'text' | 'search' | 'url' | 'tel' | 'email' | 'password' | (string & {
}) = 'search'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
}/@react-aria/autocomplete:AriaSearchAutocompleteProps AriaSearchAutocompleteProps <T> {
aria-activedescendant?: string
aria-autocomplete?: 'none' | 'inline' | 'list' | 'both'
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-errormessage?: string
aria-haspopup?: boolean | 'false' | 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoCorrect?: string
autoFocus?: boolean
children: CollectionChildren<T>
defaultInputValue?: string
defaultItems?: Iterable<T>
description?: ReactNode
disabledKeys?: Iterable<Key>
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
maxLength?: number
menuTrigger?: MenuTriggerAction = 'input'
minLength?: number
name?: string
onBeforeInput?: FormEventHandler<HTMLInputElement>
onBlur?: (FocusEvent<T>) => void
- onChange?: (T) => void
onClear?: () => void
onCompositionEnd?: CompositionEventHandler<HTMLInputElement>
onCompositionStart?: CompositionEventHandler<HTMLInputElement>
onCompositionUpdate?: CompositionEventHandler<HTMLInputElement>
onCut?: ClipboardEventHandler<HTMLInputElement>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<HTMLInputElement>
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
onPaste?: ClipboardEventHandler<HTMLInputElement>
onSelect?: ReactEventHandler<HTMLInputElement>
onSubmit?: (string | null, Key | null) => void
pattern?: string
placeholder?: string
spellCheck?: string
type?: 'text' | 'search' | 'url' | 'tel' | 'email' | 'password' | (string & {
}) = 'search'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
}@react-aria/combobox/@react-aria/combobox:useComboBox-useComboBox <T> {
+useComboBox <M extends SelectionMode = 'single', T> {
- props: AriaComboBoxOptions<T>
- state: ComboBoxState<T>
+ props: AriaComboBoxOptions<T, M>
+ state: ComboBoxState<T, M>
returnVal: undefined
}/@react-aria/combobox:AriaComboBoxOptions-AriaComboBoxOptions <T> {
+AriaComboBoxOptions <M extends SelectionMode = 'single', T> {
allowsCustomValue?: boolean
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
buttonRef?: RefObject<Element | null>
defaultInputValue?: string
defaultItems?: Iterable<T>
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
form?: string
id?: string
inputRef: RefObject<HTMLInputElement | null>
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
keyboardDelegate?: KeyboardDelegate
label?: ReactNode
layoutDelegate?: LayoutDelegate
listBoxRef: RefObject<HTMLElement | null>
menuTrigger?: MenuTriggerAction = 'input'
name?: string
onBlur?: (FocusEvent<HTMLInputElement>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<HTMLInputElement>) => void
onFocusChange?: (boolean) => void
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
popoverRef: RefObject<Element | null>
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFocusWrap?: boolean
validate?: (ComboBoxValidationValue) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
}/@react-aria/combobox:ComboBoxAria ComboBoxAria <T> {
buttonProps: AriaButtonProps
descriptionProps: DOMAttributes
errorMessageProps: DOMAttributes
inputProps: InputHTMLAttributes<HTMLInputElement>
isInvalid: boolean
labelProps: DOMAttributes
listBoxProps: AriaListBoxOptions<T>
validationDetails: ValidityState
validationErrors: Array<string>
+ valueProps: DOMAttributes
}/@react-aria/combobox:AriaComboBoxProps-AriaComboBoxProps <T> {
+AriaComboBoxProps <M extends SelectionMode = 'single', T> {
allowsCustomValue?: boolean
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children: CollectionChildren<T>
defaultInputValue?: string
defaultItems?: Iterable<T>
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
form?: string
id?: string
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
menuTrigger?: MenuTriggerAction = 'input'
name?: string
onBlur?: (FocusEvent<HTMLInputElement>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<HTMLInputElement>) => void
onFocusChange?: (boolean) => void
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFocusWrap?: boolean
validate?: (ComboBoxValidationValue) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
}@react-aria/utils/@react-aria/utils:scrollIntoView scrollIntoView {
scrollView: HTMLElement
element: HTMLElement
- opts: ScrollIntoViewOpts
returnVal: undefined
}/@react-aria/utils:scrollIntoViewport scrollIntoViewport {
targetElement: Element | null
- opts: ScrollIntoViewportOpts
+ opts?: ScrollIntoViewportOpts
returnVal: undefined
}@react-spectrum/autocomplete/@react-spectrum/autocomplete:SearchAutocomplete SearchAutocomplete <T extends {}> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
align?: 'start' | 'end' = 'start'
alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
aria-activedescendant?: string
aria-autocomplete?: 'none' | 'inline' | 'list' | 'both'
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-errormessage?: string
aria-haspopup?: boolean | 'false' | 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoCorrect?: string
autoFocus?: boolean
bottom?: Responsive<DimensionValue>
children: CollectionChildren<{}>
contextualHelp?: ReactNode
defaultInputValue?: string
defaultItems?: Iterable<{}>
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
end?: Responsive<DimensionValue>
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
flex?: Responsive<string | number | boolean>
flexBasis?: Responsive<number | string>
flexGrow?: Responsive<number>
flexShrink?: Responsive<number>
form?: string
gridArea?: Responsive<string>
gridColumn?: Responsive<string>
gridColumnEnd?: Responsive<string>
gridColumnStart?: Responsive<string>
gridRow?: Responsive<string>
gridRowEnd?: Responsive<string>
gridRowStart?: Responsive<string>
height?: Responsive<DimensionValue>
icon?: ReactElement | null
id?: string
inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
inputValue?: string
isDisabled?: boolean
isHidden?: Responsive<boolean>
isQuiet?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<{}>
justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
left?: Responsive<DimensionValue>
loadingState?: LoadingState
margin?: Responsive<DimensionValue>
marginBottom?: Responsive<DimensionValue>
marginEnd?: Responsive<DimensionValue>
marginStart?: Responsive<DimensionValue>
marginTop?: Responsive<DimensionValue>
marginX?: Responsive<DimensionValue>
marginY?: Responsive<DimensionValue>
maxHeight?: Responsive<DimensionValue>
maxLength?: number
maxWidth?: Responsive<DimensionValue>
menuTrigger?: MenuTriggerAction = 'input'
menuWidth?: DimensionValue
minHeight?: Responsive<DimensionValue>
minLength?: number
minWidth?: Responsive<DimensionValue>
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<HTMLInputElement>
onBlur?: (FocusEvent<{}>) => void
- onChange?: ({}) => void
onClear?: () => void
onCompositionEnd?: CompositionEventHandler<HTMLInputElement>
onCompositionStart?: CompositionEventHandler<HTMLInputElement>
onCompositionUpdate?: CompositionEventHandler<HTMLInputElement>
onCut?: ClipboardEventHandler<HTMLInputElement>
onFocus?: (FocusEvent<{}>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<HTMLInputElement>
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => void
onOpenChange?: (boolean, MenuTriggerAction) => void
onPaste?: ClipboardEventHandler<HTMLInputElement>
onSelect?: ReactEventHandler<HTMLInputElement>
onSubmit?: (string | null, Key | null) => void
order?: Responsive<number>
pattern?: string
position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
right?: Responsive<DimensionValue>
shouldFlip?: boolean = true
spellCheck?: string
start?: Responsive<DimensionValue>
top?: Responsive<DimensionValue>
type?: 'text' | 'search' | 'url' | 'tel' | 'email' | 'password' | (string & {
}) = 'search'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
validationState?: ValidationState
width?: Responsive<DimensionValue>
zIndex?: Responsive<number>
}/@react-spectrum/autocomplete:SpectrumSearchAutocompleteProps SpectrumSearchAutocompleteProps <T> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
align?: 'start' | 'end' = 'start'
alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
aria-activedescendant?: string
aria-autocomplete?: 'none' | 'inline' | 'list' | 'both'
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-errormessage?: string
aria-haspopup?: boolean | 'false' | 'true' | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoCorrect?: string
autoFocus?: boolean
bottom?: Responsive<DimensionValue>
children: CollectionChildren<T>
contextualHelp?: ReactNode
defaultInputValue?: string
defaultItems?: Iterable<T>
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
end?: Responsive<DimensionValue>
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
flex?: Responsive<string | number | boolean>
flexBasis?: Responsive<number | string>
flexGrow?: Responsive<number>
flexShrink?: Responsive<number>
form?: string
gridArea?: Responsive<string>
gridColumn?: Responsive<string>
gridColumnEnd?: Responsive<string>
gridColumnStart?: Responsive<string>
gridRow?: Responsive<string>
gridRowEnd?: Responsive<string>
gridRowStart?: Responsive<string>
height?: Responsive<DimensionValue>
icon?: ReactElement | null
id?: string
inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
inputValue?: string
isDisabled?: boolean
isHidden?: Responsive<boolean>
isQuiet?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
left?: Responsive<DimensionValue>
loadingState?: LoadingState
margin?: Responsive<DimensionValue>
marginBottom?: Responsive<DimensionValue>
marginEnd?: Responsive<DimensionValue>
marginStart?: Responsive<DimensionValue>
marginTop?: Responsive<DimensionValue>
marginX?: Responsive<DimensionValue>
marginY?: Responsive<DimensionValue>
maxHeight?: Responsive<DimensionValue>
maxLength?: number
maxWidth?: Responsive<DimensionValue>
menuTrigger?: MenuTriggerAction = 'input'
menuWidth?: DimensionValue
minHeight?: Responsive<DimensionValue>
minLength?: number
minWidth?: Responsive<DimensionValue>
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<HTMLInputElement>
onBlur?: (FocusEvent<T>) => void
- onChange?: (T) => void
onClear?: () => void
onCompositionEnd?: CompositionEventHandler<HTMLInputElement>
onCompositionStart?: CompositionEventHandler<HTMLInputElement>
onCompositionUpdate?: CompositionEventHandler<HTMLInputElement>
onCut?: ClipboardEventHandler<HTMLInputElement>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<HTMLInputElement>
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => void
onOpenChange?: (boolean, MenuTriggerAction) => void
onPaste?: ClipboardEventHandler<HTMLInputElement>
onSelect?: ReactEventHandler<HTMLInputElement>
onSubmit?: (string | null, Key | null) => void
order?: Responsive<number>
pattern?: string
position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
right?: Responsive<DimensionValue>
shouldFlip?: boolean = true
spellCheck?: string
start?: Responsive<DimensionValue>
top?: Responsive<DimensionValue>
type?: 'text' | 'search' | 'url' | 'tel' | 'email' | 'password' | (string & {
}) = 'search'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
validationState?: ValidationState
width?: Responsive<DimensionValue>
zIndex?: Responsive<number>
}@react-spectrum/table/@react-spectrum/table:TableHeaderProps TableHeaderProps <T> {
children: ColumnElement<T> | Array<ColumnElement<T>> | ColumnRenderer<T>
- columns?: readonly Array<T>
+ columns?: Array<T>
}@react-stately/combobox/@react-stately/combobox:useComboBoxState-useComboBoxState <T extends {}> {
+useComboBoxState <M extends SelectionMode = 'single', T extends {}> {
- props: ComboBoxStateOptions<T>
+ props: ComboBoxStateOptions<T, M>
returnVal: undefined
}/@react-stately/combobox:ComboBoxStateOptions-ComboBoxStateOptions <T> {
+ComboBoxStateOptions <M extends SelectionMode = 'single', T> {
allowsCustomValue?: boolean
allowsEmptyCollection?: boolean
autoFocus?: boolean
collection?: Collection<Node<T>>
defaultFilter?: FilterFn
defaultInputValue?: string
defaultItems?: Iterable<T>
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
inputValue?: string
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
menuTrigger?: MenuTriggerAction = 'input'
onBlur?: (FocusEvent<HTMLInputElement>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<HTMLInputElement>) => void
onFocusChange?: (boolean) => void
onInputChange?: (string) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean, MenuTriggerAction) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldCloseOnBlur?: boolean
validate?: (ComboBoxValidationValue) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
}/@react-stately/combobox:ComboBoxState-ComboBoxState <T> {
+ComboBoxState <M extends SelectionMode = 'single', T> {
close: () => void
collection: Collection<Node<T>>
commit: () => void
commitValidation: () => void
defaultInputValue: string
- defaultSelectedKey: Key | null
+ defaultValue: ValueType<SelectionMode>
disabledKeys: Set<Key>
displayValidation: ValidationResult
focusStrategy: FocusStrategy | null
inputValue: string
isFocused: boolean
isOpen: boolean
open: (FocusStrategy | null, MenuTriggerAction) => void
realtimeValidation: ValidationResult
resetValidation: () => void
revert: () => void
- selectedItem: Node<T> | null
- selectedKey: Key | null
+ selectedItems: Array<Node<T>>
selectionManager: SelectionManager
setFocused: (boolean) => void
setInputValue: (string) => void
setOpen: (boolean) => void
- setSelectedKey: (Key | null) => void
+ setValue: (Key | readonly Array<Key> | null) => void
toggle: (FocusStrategy | null, MenuTriggerAction) => void
updateValidation: (ValidationResult) => void
+ value: ValueType<SelectionMode>
}@react-stately/table/@react-stately/table:TableHeaderProps TableHeaderProps <T> {
children: ColumnElement<T> | Array<ColumnElement<T>> | ColumnRenderer<T>
- columns?: readonly Array<T>
+ columns?: Array<T>
} |
_cough_ years later _cough_ they finally shipped it! adobe/react-spectrum#9525 🎉
Closes #2140
Similar to #8734, this adds support for the
selectionModeprop toComboBox, deprecatesselectedKey/onSelectionChange, and addsvalue/onChangewhich can be arrays in multi-select mode. When multi-select is enabled, the input value is cleared when an item is selected. It's the dev's responsibility to render the selected items somehow, e.g. using a TagGroup. Eventually we will have TagField which will improve this further to support tags that are "inline" with the input, but using an adjacent TagGroup is fine for now.To do:
ComboBoxStateContextto get the selected items but we could add a more first-class API for this.selectionMode/value/onChangefrom S2 props for now? Do we want to support this case or wait for TagField?