-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: Add support for multiple selection to Select and Picker #8734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
/** The textValue of the currently selected item. */ | ||
selectedText: string | null | ||
/** The object values of the currently selected items. */ | ||
selectedItems: (T | null)[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this to include null? The other option would be to filter out items that didn't set a value
on the ListBoxItem
. But then we'd need some other way of knowing the total selected item count at least.
Build successful! 🎉 |
Build successful! 🎉 |
Build successful! 🎉 |
if (e.target.multiple) { | ||
setValue(Array.from( | ||
e.target.selectedOptions, | ||
(option) => option.value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think currently we allow for Key
which is string | number
but this would change it to string
only right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's already the case today with HiddenSelect since the DOM only accepts strings.
if (selectionMode === 'single') { | ||
let key = keys.values().next().value ?? null; | ||
setValue(key); | ||
triggerState.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combine work to keep open with #8733 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah we will but we can merge them separately
# Conflicts: # packages/@react-spectrum/s2/stories/Picker.stories.tsx # packages/@react-stately/combobox/package.json # packages/@react-stately/select/package.json # yarn.lock
Build successful! 🎉 |
## API Changes
react-aria-components/react-aria-components:Select-Select <T extends {} = {
+Select <M extends SelectionMode = 'single', T extends {} = {
}> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
children?: ChildrenOrFunction<SelectRenderProps>
className?: ClassNameOrFunction<SelectRenderProps>
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
disabledKeys?: Iterable<Key>
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string = 'Select an item' (localized)
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
slot?: string | null
style?: StyleOrFunction<SelectRenderProps>
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
} /react-aria-components:SelectProps-SelectProps <T extends {} = {
+SelectProps <M extends SelectionMode = 'single', T extends {} = {
}> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
children?: ChildrenOrFunction<SelectRenderProps>
className?: ClassNameOrFunction<SelectRenderProps>
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
disabledKeys?: Iterable<Key>
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string = 'Select an item' (localized)
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
slot?: string | null
style?: StyleOrFunction<SelectRenderProps>
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
} /react-aria-components:SelectValueRenderProps SelectValueRenderProps <T> {
isPlaceholder: boolean
- selectedItem: T | null
- selectedText: string | null
+ selectedItems: Array<T | null>
+ selectedText: string
+ state: SelectState<T, 'single' | 'multiple'>
} /react-aria-components:SelectState-SelectState <T> {
+SelectState <M extends SelectionMode = 'single', T> {
close: () => void
collection: Collection<Node<T>>
commitValidation: () => void
- defaultSelectedKey: Key | null
+ defaultValue: ValueType<SelectionMode>
disabledKeys: Set<Key>
displayValidation: ValidationResult
focusStrategy: FocusStrategy | null
isFocused: boolean
isOpen: boolean
open: (FocusStrategy | null) => void
realtimeValidation: ValidationResult
resetValidation: () => void
- selectedItem: Node<T> | null
- selectedKey: Key | null
+ selectedItems: Array<Node<T>>
selectionManager: SelectionManager
setFocused: (boolean) => void
setOpen: (boolean) => void
- setSelectedKey: (Key | null) => void
+ setValue: (Key | Array<Key> | null) => void
toggle: (FocusStrategy | null) => void
updateValidation: (ValidationResult) => void
+ value: ValueType<SelectionMode>
} @react-aria/select/@react-aria/select:useSelect-useSelect <T> {
+useSelect <M extends SelectionMode = 'single', T> {
- props: AriaSelectOptions<T>
- state: SelectState<T>
+ props: AriaSelectOptions<T, M>
+ state: SelectState<T, M>
ref: RefObject<HTMLElement | null>
returnVal: undefined
} /@react-aria/select:useHiddenSelect-useHiddenSelect <T> {
+useHiddenSelect <M extends SelectionMode = 'single', T> {
props: AriaHiddenSelectOptions
- state: SelectState<T>
+ state: SelectState<T, M>
triggerRef: RefObject<FocusableElement | null>
returnVal: undefined
} /@react-aria/select:HiddenSelect-HiddenSelect <T> {
+HiddenSelect <M extends SelectionMode = 'single', T> {
autoComplete?: string
form?: string
isDisabled?: boolean
label?: ReactNode
name?: string
- state: SelectState<T>
+ state: SelectState<T, SelectionMode>
triggerRef: RefObject<FocusableElement | null>
} /@react-aria/select:AriaSelectOptions-AriaSelectOptions <T> {
+AriaSelectOptions <M extends SelectionMode = 'single', T> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
keyboardDelegate?: KeyboardDelegate
label?: ReactNode
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
- validate?: (Key) => ValidationError | boolean | null | undefined
+ selectionMode?: SelectionMode = 'single'
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
} /@react-aria/select:SelectAria-SelectAria <T> {
+SelectAria <M extends SelectionMode = 'single', T> {
descriptionProps: DOMAttributes
errorMessageProps: DOMAttributes
- hiddenSelectProps: HiddenSelectProps<T>
+ hiddenSelectProps: HiddenSelectProps<T, SelectionMode>
isInvalid: boolean
labelProps: DOMAttributes
menuProps: AriaListBoxOptions<T>
triggerProps: AriaButtonProps
validationErrors: Array<string>
valueProps: DOMAttributes
} /@react-aria/select:HiddenSelectProps-HiddenSelectProps <T> {
+HiddenSelectProps <M extends SelectionMode = 'single', T> {
autoComplete?: string
form?: string
isDisabled?: boolean
label?: ReactNode
name?: string
- state: SelectState<T>
+ state: SelectState<T, SelectionMode>
triggerRef: RefObject<FocusableElement | null>
} /@react-aria/select:AriaSelectProps-AriaSelectProps <T> {
+AriaSelectProps <M extends SelectionMode = 'single', T> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
children: CollectionChildren<T>
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
- validate?: (Key) => ValidationError | boolean | null | undefined
+ selectionMode?: SelectionMode = 'single'
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
} @react-spectrum/picker/@react-spectrum/picker:Picker Picker <T extends {}> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
align?: Alignment = 'start'
alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
bottom?: Responsive<DimensionValue>
children: CollectionChildren<{}>
contextualHelp?: ReactNode
defaultOpen?: boolean
defaultSelectedKey?: Key
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
end?: Responsive<DimensionValue>
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>
id?: string
isDisabled?: boolean
isHidden?: Responsive<boolean>
isInvalid?: boolean
isLoading?: boolean
isOpen?: boolean
isQuiet?: 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>
margin?: Responsive<DimensionValue>
marginBottom?: Responsive<DimensionValue>
marginEnd?: Responsive<DimensionValue>
marginStart?: Responsive<DimensionValue>
marginTop?: Responsive<DimensionValue>
marginX?: Responsive<DimensionValue>
marginY?: Responsive<DimensionValue>
maxHeight?: Responsive<DimensionValue>
maxWidth?: Responsive<DimensionValue>
menuWidth?: DimensionValue
minHeight?: Responsive<DimensionValue>
minWidth?: Responsive<DimensionValue>
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => any
onOpenChange?: (boolean) => void
onSelectionChange?: (Key | null) => void
order?: Responsive<number>
placeholder?: string
position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
right?: Responsive<DimensionValue>
selectedKey?: Key | null
shouldFlip?: boolean = true
start?: Responsive<DimensionValue>
top?: Responsive<DimensionValue>
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
width?: Responsive<DimensionValue>
zIndex?: Responsive<number>
} /@react-spectrum/picker:SpectrumPickerProps SpectrumPickerProps <T> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
align?: Alignment = 'start'
alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
bottom?: Responsive<DimensionValue>
children: CollectionChildren<T>
contextualHelp?: ReactNode
defaultOpen?: boolean
defaultSelectedKey?: Key
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
end?: Responsive<DimensionValue>
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>
id?: string
isDisabled?: boolean
isHidden?: Responsive<boolean>
isInvalid?: boolean
isLoading?: boolean
isOpen?: boolean
isQuiet?: 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>
margin?: Responsive<DimensionValue>
marginBottom?: Responsive<DimensionValue>
marginEnd?: Responsive<DimensionValue>
marginStart?: Responsive<DimensionValue>
marginTop?: Responsive<DimensionValue>
marginX?: Responsive<DimensionValue>
marginY?: Responsive<DimensionValue>
maxHeight?: Responsive<DimensionValue>
maxWidth?: Responsive<DimensionValue>
menuWidth?: DimensionValue
minHeight?: Responsive<DimensionValue>
minWidth?: Responsive<DimensionValue>
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => any
onOpenChange?: (boolean) => void
onSelectionChange?: (Key | null) => void
order?: Responsive<number>
placeholder?: string
position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
right?: Responsive<DimensionValue>
selectedKey?: Key | null
shouldFlip?: boolean = true
start?: Responsive<DimensionValue>
top?: Responsive<DimensionValue>
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
width?: Responsive<DimensionValue>
zIndex?: Responsive<number>
} @react-spectrum/s2/@react-spectrum/s2:Picker-Picker <T extends {}> {
+Picker <M extends SelectionMode = 'single', T extends {}> {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
align?: 'start' | 'end' = 'start'
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
children: ReactNode | ({}) => ReactNode
contextualHelp?: ReactNode
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
dependencies?: ReadonlyArray<any>
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
loadingState?: LoadingState
menuWidth?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => any
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string = 'Select an item' (localized)
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFlip?: boolean = true
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
styles?: StylesProp
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
} /@react-spectrum/s2:PickerProps-PickerProps <T extends {}> {
+PickerProps <M extends SelectionMode = 'single', T extends {}> {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
align?: 'start' | 'end' = 'start'
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoComplete?: string
autoFocus?: boolean
children: ReactNode | ({}) => ReactNode
contextualHelp?: ReactNode
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
dependencies?: ReadonlyArray<any>
description?: ReactNode
direction?: 'bottom' | 'top' = 'bottom'
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
excludeFromTabOrder?: boolean
form?: string
id?: string
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
loadingState?: LoadingState
menuWidth?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onLoadMore?: () => any
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string = 'Select an item' (localized)
- selectedKey?: Key | null
+ selectionMode?: SelectionMode = 'single'
shouldFlip?: boolean = true
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
styles?: StylesProp
- validate?: (Key) => ValidationError | boolean | null | undefined
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
+ value?: ValueType<SelectionMode>
} @react-stately/select/@react-stately/select:useSelectState-useSelectState <T extends {}> {
+useSelectState <M extends SelectionMode = 'single', T extends {}> {
- props: SelectStateOptions<T>
+ props: SelectStateOptions<T, M>
returnVal: undefined
} /@react-stately/select:SelectProps-SelectProps <T> {
+SelectProps <M extends SelectionMode = 'single', T> {
autoFocus?: boolean
children: CollectionChildren<T>
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
- validate?: (Key) => ValidationError | boolean | null | undefined
+ selectionMode?: SelectionMode = 'single'
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
} /@react-stately/select:SelectState-SelectState <T> {
+SelectState <M extends SelectionMode = 'single', T> {
close: () => void
collection: Collection<Node<T>>
commitValidation: () => void
- defaultSelectedKey: Key | null
+ defaultValue: ValueType<SelectionMode>
disabledKeys: Set<Key>
displayValidation: ValidationResult
focusStrategy: FocusStrategy | null
isFocused: boolean
isOpen: boolean
open: (FocusStrategy | null) => void
realtimeValidation: ValidationResult
resetValidation: () => void
- selectedItem: Node<T> | null
- selectedKey: Key | null
+ selectedItems: Array<Node<T>>
selectionManager: SelectionManager
setFocused: (boolean) => void
setOpen: (boolean) => void
- setSelectedKey: (Key | null) => void
+ setValue: (Key | Array<Key> | null) => void
toggle: (FocusStrategy | null) => void
updateValidation: (ValidationResult) => void
+ value: ValueType<SelectionMode>
} /@react-stately/select:SelectStateOptions-SelectStateOptions <T> {
+SelectStateOptions <M extends SelectionMode = 'single', T> {
autoFocus?: boolean
collection?: Collection<Node<T>>
defaultOpen?: boolean
- defaultSelectedKey?: Key
+ defaultValue?: ValueType<SelectionMode>
description?: ReactNode
disabledKeys?: Iterable<Key>
errorMessage?: ReactNode | (ValidationResult) => ReactNode
isDisabled?: boolean
isInvalid?: boolean
isOpen?: boolean
isRequired?: boolean
items?: Iterable<T>
label?: ReactNode
onBlur?: (FocusEvent<Target>) => void
+ onChange?: (T) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onOpenChange?: (boolean) => void
- onSelectionChange?: (Key | null) => void
placeholder?: string
- selectedKey?: Key | null
- validate?: (Key) => ValidationError | boolean | null | undefined
+ selectionMode?: SelectionMode = 'single'
+ validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
validationBehavior?: 'aria' | 'native' = 'aria'
+ value?: ValueType<SelectionMode>
} |
} | ||
}; | ||
|
||
let listState = useListState({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty exciting change! Question tho, Will ComboBox
eventually have the same support for multiple selection? And if it were to be done, i'd imagine the changes needed for it would also be something like this? Changing from useSingleSelectListState
to useListState
+ wiring up the other parts etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, possibly. There are some differences with ComboBox though. You'd need some way to display the selected items outside the text input, typically tags of some kind. It may end up being a separate TagField component in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any update on when we might see this stuff released? Specifically the Combobox/TagField implementations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only thing I found in testing is that the async select example in RAC storybook isn't hooked up correctly, it only does single select. I doubt it's a bug in the logic, more likely just needs to be passed through
Closes #8738
This adds support for selecting multiple items to RAC Select and S2 Picker. By default, the selected items are concatenated into a comma separated list. Using RAC SelectValue's render props, you can customize this to whatever string you want (e.g. "2 selected items"). Behavior is TBD for Spectrum.
The API is changing from using
selectedKey
to usingvalue
. When multi-select is enabled,value
accepts an array instead of a single id. This matches the native React DOM<select>
API. The old API is supported for backward compatibility, but only applies to single selection.Behaviorally, it uses the existing ListBox component which already supports multi-select. Typeahead and arrow key on the button while the select is closed is disabled when using multi-select, and the popover stays open after selection to facilitate selecting multiple items.