Skip to content

feat: Textfield prefix followup#10014

Open
snowystinger wants to merge 6 commits intomainfrom
textfield-prefix-followup
Open

feat: Textfield prefix followup#10014
snowystinger wants to merge 6 commits intomainfrom
textfield-prefix-followup

Conversation

@snowystinger
Copy link
Copy Markdown
Member

Closes

Team talked about supporting more things in the prefix slot today.

It center baselines everything automatically for alignment. Should it also set size on avatar and swatch? that's more things that each field has to pull in.

This also adds an id to associate with the input's aria-labelledby so it's announced when a user arrives in an input.

✅ 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:

🧢 Your Project:

@rspbot
Copy link
Copy Markdown

rspbot commented May 5, 2026

@rspbot
Copy link
Copy Markdown

rspbot commented May 5, 2026

## API Changes

@react-spectrum/s2

/@react-spectrum/s2:ColorField

 ColorField {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-errormessage?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   channel?: ColorChannel
   colorSpace?: ColorSpace
   contextualHelp?: ReactNode
   defaultValue?: T
   description?: ReactNode
   errorMessage?: ReactNode | (ValidationResult) => ReactNode
   excludeFromTabOrder?: boolean
   form?: string
   id?: string
   isDisabled?: boolean
   isInvalid?: boolean
   isReadOnly?: boolean
   isRequired?: boolean
   isWheelDisabled?: boolean
   label?: ReactNode
   labelAlign?: Alignment = 'start'
   labelPosition?: LabelPosition = 'top'
   name?: string
   necessityIndicator?: NecessityIndicator = 'icon'
   onBeforeInput?: FormEventHandler<T>
   onBlur?: (FocusEvent<Target>) => void
   onChange?: (Color | null) => void
   onCompositionEnd?: CompositionEventHandler<T>
   onCompositionStart?: CompositionEventHandler<T>
   onCompositionUpdate?: CompositionEventHandler<T>
   onCopy?: ClipboardEventHandler<T>
   onCut?: ClipboardEventHandler<T>
   onFocus?: (FocusEvent<Target>) => void
   onFocusChange?: (boolean) => void
   onInput?: FormEventHandler<T>
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onPaste?: ClipboardEventHandler<T>
   onSelect?: ReactEventHandler<T>
   placeholder?: string
+  prefix?: ReactNode
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   styles?: StylesProp
   validate?: (Color | null) => ValidationError | boolean | null | undefined
   value?: T
 }

/@react-spectrum/s2:ComboBox

 ComboBox <T extends {}> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   align?: 'start' | 'end' = 'start'
   allowsCustomValue?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children: ReactNode | ({}) => ReactNode
   contextualHelp?: ReactNode
   defaultInputValue?: string
   defaultItems?: Iterable<T>
   defaultSelectedKey?: Key | null
   dependencies?: ReadonlyArray<any>
   description?: ReactNode
   direction?: 'bottom' | 'top' = 'bottom'
   disabledKeys?: Iterable<Key>
   errorMessage?: ReactNode | (ValidationResult) => ReactNode
   form?: string
   formValue?: 'text' | 'key' = 'key'
   id?: string
   inputValue?: string
   isDisabled?: boolean
   isInvalid?: boolean
   isReadOnly?: boolean
   isRequired?: boolean
   items?: Iterable<T>
   label?: ReactNode
   labelAlign?: Alignment = 'start'
   labelPosition?: LabelPosition = 'top'
   loadingState?: LoadingState
   menuTrigger?: MenuTriggerAction = 'input'
   menuWidth?: number
   name?: string
   necessityIndicator?: NecessityIndicator = 'icon'
   onBlur?: (FocusEvent<HTMLInputElement>) => void
   onFocus?: (FocusEvent<HTMLInputElement>) => void
   onFocusChange?: (boolean) => void
   onInputChange?: (string) => void
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onLoadMore?: () => any
   onOpenChange?: (boolean, MenuTriggerAction) => void
   onSelectionChange?: (Key | null) => void
   placeholder?: string
+  prefix?: ReactNode
   selectedKey?: Key | null
   shouldFlip?: boolean = true
   shouldFocusWrap?: boolean
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   styles?: StylesProp
   validate?: (ComboBoxValidationValue<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
 }

/@react-spectrum/s2:NumberField

 NumberField {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   commitBehavior?: 'snap' | 'validate' = 'snap'
   contextualHelp?: ReactNode
   decrementAriaLabel?: string
   defaultValue?: number
   description?: ReactNode
   errorMessage?: ReactNode | (ValidationResult) => ReactNode
   form?: string
   formatOptions?: Intl.NumberFormatOptions
   hideStepper?: boolean = false
   id?: string
   incrementAriaLabel?: string
   isDisabled?: boolean
   isInvalid?: boolean
   isReadOnly?: boolean
   isRequired?: boolean
   isWheelDisabled?: boolean
   label?: ReactNode
   labelAlign?: Alignment = 'start'
   labelPosition?: LabelPosition = 'top'
   maxValue?: number
   minValue?: number
   name?: string
   necessityIndicator?: NecessityIndicator = 'icon'
   onBeforeInput?: FormEventHandler<T>
   onBlur?: (FocusEvent<Target>) => void
   onChange?: (T) => void
   onCompositionEnd?: CompositionEventHandler<T>
   onCompositionStart?: CompositionEventHandler<T>
   onCompositionUpdate?: CompositionEventHandler<T>
   onCopy?: ClipboardEventHandler<T>
   onCut?: ClipboardEventHandler<T>
   onFocus?: (FocusEvent<Target>) => void
   onFocusChange?: (boolean) => void
   onInput?: FormEventHandler<T>
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onPaste?: ClipboardEventHandler<T>
   onSelect?: ReactEventHandler<T>
   placeholder?: string
+  prefix?: ReactNode
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   step?: number
   styles?: StylesProp
   validationBehavior?: 'native' | 'aria' = 'native'
   value?: number
 }

/@react-spectrum/s2:ColorFieldProps

 ColorFieldProps {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-errormessage?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   channel?: ColorChannel
   colorSpace?: ColorSpace
   contextualHelp?: ReactNode
   defaultValue?: T
   description?: ReactNode
   errorMessage?: ReactNode | (ValidationResult) => ReactNode
   excludeFromTabOrder?: boolean
   form?: string
   id?: string
   isDisabled?: boolean
   isInvalid?: boolean
   isReadOnly?: boolean
   isRequired?: boolean
   isWheelDisabled?: boolean
   label?: ReactNode
   labelAlign?: Alignment = 'start'
   labelPosition?: LabelPosition = 'top'
   name?: string
   necessityIndicator?: NecessityIndicator = 'icon'
   onBeforeInput?: FormEventHandler<T>
   onBlur?: (FocusEvent<Target>) => void
   onChange?: (Color | null) => void
   onCompositionEnd?: CompositionEventHandler<T>
   onCompositionStart?: CompositionEventHandler<T>
   onCompositionUpdate?: CompositionEventHandler<T>
   onCopy?: ClipboardEventHandler<T>
   onCut?: ClipboardEventHandler<T>
   onFocus?: (FocusEvent<Target>) => void
   onFocusChange?: (boolean) => void
   onInput?: FormEventHandler<T>
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onPaste?: ClipboardEventHandler<T>
   onSelect?: ReactEventHandler<T>
   placeholder?: string
+  prefix?: ReactNode
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   styles?: StylesProp
   validate?: (Color | null) => ValidationError | boolean | null | undefined
   value?: T
 }

/@react-spectrum/s2:ComboBoxProps

 ComboBoxProps <T extends {}> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   align?: 'start' | 'end' = 'start'
   allowsCustomValue?: boolean
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean
   children: ReactNode | ({}) => ReactNode
   contextualHelp?: ReactNode
   defaultInputValue?: string
   defaultItems?: Iterable<T>
   defaultSelectedKey?: Key | null
   dependencies?: ReadonlyArray<any>
   description?: ReactNode
   direction?: 'bottom' | 'top' = 'bottom'
   disabledKeys?: Iterable<Key>
   errorMessage?: ReactNode | (ValidationResult) => ReactNode
   form?: string
   formValue?: 'text' | 'key' = 'key'
   id?: string
   inputValue?: string
   isDisabled?: boolean
   isInvalid?: boolean
   isReadOnly?: boolean
   isRequired?: boolean
   items?: Iterable<T>
   label?: ReactNode
   labelAlign?: Alignment = 'start'
   labelPosition?: LabelPosition = 'top'
   loadingState?: LoadingState
   menuTrigger?: MenuTriggerAction = 'input'
   menuWidth?: number
   name?: string
   necessityIndicator?: NecessityIndicator = 'icon'
   onBlur?: (FocusEvent<HTMLInputElement>) => void
   onFocus?: (FocusEvent<HTMLInputElement>) => void
   onFocusChange?: (boolean) => void
   onInputChange?: (string) => void
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onLoadMore?: () => any
   onOpenChange?: (boolean, MenuTriggerAction) => void
   onSelectionChange?: (Key | null) => void
   placeholder?: string
+  prefix?: ReactNode
   selectedKey?: Key | null
   shouldFlip?: boolean = true
   shouldFocusWrap?: boolean
   size?: 'S' | 'M' | 'L' | 'XL' = 'M'
   styles?: StylesProp
   validate?: (ComboBoxValidationValue<SelectionMode>) => ValidationError | boolean | null | undefined
   validationBehavior?: 'native' | 'aria' = 'native'
 }

@snowystinger snowystinger mentioned this pull request May 5, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants