Skip to content

Conversation

@LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Oct 21, 2025

From testing

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

Try tapping on a S2 TextArea's invalid icon via a mobile device and verify that your virtual keyboard appears and you can then type into the field.

🧢 Your Project:

RSP

@rspbot
Copy link

rspbot commented Oct 21, 2025

Copy link
Member

@reidbarber reidbarber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When tapping the invalid icon (or below it) on iPhone, the text area doesn't get focused for me.

@LFDanLu
Copy link
Member Author

LFDanLu commented Oct 21, 2025

@reidbarber hrm, I might need some help with the iPhone testing then, it works on my Android device and I don't have a iOS 26 compatible device :/.

@rspbot
Copy link

rspbot commented Oct 21, 2025

@rspbot
Copy link

rspbot commented Oct 21, 2025

## API Changes

react-aria-components

/react-aria-components:Select

 Select <M extends SelectionMode = 'single', T extends {} = {
   
 }> {
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children?: ChildrenOrFunction<SelectRenderProps>
   className?: ClassNameOrFunction<SelectRenderProps> = 'react-aria-Select'
   defaultOpen?: boolean
   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
   placeholder?: string = 'Select an item' (localized)
   selectionMode?: SelectionMode = 'single'
   slot?: string | null
   style?: StyleOrFunction<SelectRenderProps>
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
   value?: ValueType<SelectionMode>
 }

/react-aria-components:SelectProps

 SelectProps <M extends SelectionMode = 'single', T extends {} = {
   
 }> {
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children?: ChildrenOrFunction<SelectRenderProps>
   className?: ClassNameOrFunction<SelectRenderProps> = 'react-aria-Select'
   defaultOpen?: boolean
   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
   placeholder?: string = 'Select an item' (localized)
   selectionMode?: SelectionMode = 'single'
   slot?: string | null
   style?: StyleOrFunction<SelectRenderProps>
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
   value?: ValueType<SelectionMode>
 }

@react-aria/select

/@react-aria/select:AriaSelectOptions

 AriaSelectOptions <M extends SelectionMode = 'single', T> {
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   defaultOpen?: boolean
   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
   placeholder?: string
   selectionMode?: SelectionMode = 'single'
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'aria' | 'native' = 'aria'
   value?: ValueType<SelectionMode>
 }

/@react-aria/select:AriaSelectProps

 AriaSelectProps <M extends SelectionMode = 'single', T> {
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children: CollectionChildren<T>
   defaultOpen?: boolean
   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
   placeholder?: string
   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'>
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: 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?: (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'>
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: 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?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'aria' | 'native' = 'aria'
   width?: Responsive<DimensionValue>
   zIndex?: Responsive<number>
 }

@react-spectrum/s2

/@react-spectrum/s2:Picker

 Picker <M extends SelectionMode = 'single', T extends {}> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   align?: 'start' | 'end' = 'start'
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children: ReactNode | ({}) => ReactNode
   contextualHelp?: ReactNode
   defaultOpen?: boolean
   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
   placeholder?: string = 'Select an item' (localized)
   selectionMode?: SelectionMode = 'single'
   shouldFlip?: boolean = true
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   styles?: StylesProp
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
   value?: ValueType<SelectionMode>
 }

/@react-spectrum/s2:PickerProps

 PickerProps <M extends SelectionMode = 'single', T extends {}> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   align?: 'start' | 'end' = 'start'
-  allowsEmptyCollection?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children: ReactNode | ({}) => ReactNode
   contextualHelp?: ReactNode
   defaultOpen?: boolean
   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
   placeholder?: string = 'Select an item' (localized)
   selectionMode?: SelectionMode = 'single'
   shouldFlip?: boolean = true
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   styles?: StylesProp
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
   value?: ValueType<SelectionMode>
 }

@react-stately/select

/@react-stately/select:SelectProps

 SelectProps <M extends SelectionMode = 'single', T> {
-  allowsEmptyCollection?: boolean
   autoFocus?: boolean
   children: CollectionChildren<T>
   defaultOpen?: boolean
   defaultValue?: ValueType<SelectionMode>
   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
   placeholder?: string
   selectionMode?: SelectionMode = 'single'
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'aria' | 'native' = 'aria'
   value?: ValueType<SelectionMode>
 }

/@react-stately/select:SelectStateOptions

 SelectStateOptions <M extends SelectionMode = 'single', T> {
-  allowsEmptyCollection?: boolean
   autoFocus?: boolean
   collection?: Collection<Node<T>>
   defaultOpen?: boolean
   defaultValue?: ValueType<SelectionMode>
   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
   placeholder?: string
   selectionMode?: SelectionMode = 'single'
   validate?: (ValidationType<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'aria' | 'native' = 'aria'
   value?: ValueType<SelectionMode>
 }

Copy link
Member

@reidbarber reidbarber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, tested on iOS Safari and MacOS Chrome.

@LFDanLu LFDanLu added this pull request to the merge queue Oct 22, 2025
Merged via the queue into main with commit 95acde1 Oct 22, 2025
32 checks passed
@LFDanLu LFDanLu deleted the testing_bug_fixes branch October 22, 2025 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants