feat: Textfield prefix#9957
Conversation
| */ | ||
| size?: 'S' | 'M' | 'L' | 'XL' | ||
| /** | ||
| * The prefix to display in the text field. Either a string or workflow icon. |
There was a problem hiding this comment.
This is all the figma currently supports, ie no buttons or loaders. No suffix yet either
There was a problem hiding this comment.
wonder if an avatar or something should be supported as well
| export const TextFieldWithAddons: StoryTextField = { | ||
| render: (args) => ( | ||
| <Form> | ||
| <TextField {...args} label="Phone Number" prefix="#" placeholder="(000) 000-0000" /> |
There was a problem hiding this comment.
Should I default add the hash to colorfield?
There was a problem hiding this comment.
doesn't it depend what color space and channel the colorfield has? E.g. hsl + hue doesn't have a hash
There was a problem hiding this comment.
Colorfield is always rgb hex, the prop just controls the color object value you get out of it
There was a problem hiding this comment.
oo right, i forgot to set the channel in addition to the color space, well, we could set it for the ones we know
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
## API Changes
@react-spectrum/s2/@react-spectrum/s2:TextArea TextArea {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
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
contextualHelp?: ReactNode
defaultValue?: string
description?: ReactNode
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'
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
maxLength?: number
minLength?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<T>
onBlur?: (FocusEvent<T>) => void
onChange?: (T) => void
onCompositionEnd?: CompositionEventHandler<T>
onCompositionStart?: CompositionEventHandler<T>
onCompositionUpdate?: CompositionEventHandler<T>
onCopy?: ClipboardEventHandler<T>
onCut?: ClipboardEventHandler<T>
onFocus?: (FocusEvent<T>) => 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
spellCheck?: string
styles?: StylesProp
validationBehavior?: 'native' | 'aria' = 'native'
value?: string
}/@react-spectrum/s2:TextField TextField {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
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
contextualHelp?: ReactNode
defaultValue?: string
description?: ReactNode
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'
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
maxLength?: number
minLength?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<T>
onBlur?: (FocusEvent<T>) => void
onChange?: (T) => void
onCompositionEnd?: CompositionEventHandler<T>
onCompositionStart?: CompositionEventHandler<T>
onCompositionUpdate?: CompositionEventHandler<T>
onCopy?: ClipboardEventHandler<T>
onCut?: ClipboardEventHandler<T>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<T>
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPaste?: ClipboardEventHandler<T>
onSelect?: ReactEventHandler<T>
pattern?: string
placeholder?: string
+ prefix?: ReactNode
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
spellCheck?: string
styles?: StylesProp
}) = 'text'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
value?: string
}/@react-spectrum/s2:TextFieldProps TextFieldProps {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
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
contextualHelp?: ReactNode
defaultValue?: string
description?: ReactNode
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'
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
maxLength?: number
minLength?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<T>
onBlur?: (FocusEvent<T>) => void
onChange?: (T) => void
onCompositionEnd?: CompositionEventHandler<T>
onCompositionStart?: CompositionEventHandler<T>
onCompositionUpdate?: CompositionEventHandler<T>
onCopy?: ClipboardEventHandler<T>
onCut?: ClipboardEventHandler<T>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<T>
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPaste?: ClipboardEventHandler<T>
onSelect?: ReactEventHandler<T>
pattern?: string
placeholder?: string
+ prefix?: ReactNode
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
spellCheck?: string
styles?: StylesProp
}) = 'text'
validate?: (string) => ValidationError | boolean | null | undefined
validationBehavior?: 'native' | 'aria' = 'native'
value?: string
}/@react-spectrum/s2:TextAreaProps TextAreaProps {
UNSAFE_className?: UnsafeClassName
UNSAFE_style?: CSSProperties
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
contextualHelp?: ReactNode
defaultValue?: string
description?: ReactNode
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'
isDisabled?: boolean
isInvalid?: boolean
isReadOnly?: boolean
isRequired?: boolean
label?: ReactNode
labelAlign?: Alignment = 'start'
labelPosition?: LabelPosition = 'top'
maxLength?: number
minLength?: number
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<T>
onBlur?: (FocusEvent<T>) => void
onChange?: (T) => void
onCompositionEnd?: CompositionEventHandler<T>
onCompositionStart?: CompositionEventHandler<T>
onCompositionUpdate?: CompositionEventHandler<T>
onCopy?: ClipboardEventHandler<T>
onCut?: ClipboardEventHandler<T>
onFocus?: (FocusEvent<T>) => 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
spellCheck?: string
styles?: StylesProp
validationBehavior?: 'native' | 'aria' = 'native'
value?: string
} |
| render: (args) => ( | ||
| <Form> | ||
| <TextField {...args} label="Phone Number" prefix="#" placeholder="(000) 000-0000" /> | ||
| <TextField {...args} label="URL" prefix="https://" placeholder="example.com" /> |
There was a problem hiding this comment.
is it weird that the prefix text is not quite at the same level as the placeholder text?
There was a problem hiding this comment.
hmm I see this in the docs via https://d1pzu54gtk2aed.cloudfront.net/pr/a670e569138a9388c25d2ae0acbd545c3e5b6050/TextField:

I swear I saw it in the storybook too, but doesn't seem like it anymore
There was a problem hiding this comment.
I'm trying it in the docs, I cannot reproduce. What browser are you in and what version?
There was a problem hiding this comment.
Chrome 147.0.7727.117, MacOS Tahoe. If you can't reproduce and are on the same version then happy to approve and we can test it further in testing
There was a problem hiding this comment.
Tried on 147.0.7727.102 and 147.0.7727.138 on MacOS Tahoe, but I'm not seeing it in Chrome
I tried Firefox and Safari as well. I only see the issue in Safari.
I can't see any reason this should happen, it could just be a difference in how inputs render. I tried adjusting line-height to match, but I'm worried that's a bigger change than I want to tackle here since it was needed inside Field.tsx. I also tried removing flex and using vertical align baseline with no success.
It looks like it's .5px, so I'm going to recommend we continue and we can try to fix it later.
| export const TextFieldWithAddons: StoryTextField = { | ||
| render: (args) => ( | ||
| <Form> | ||
| <TextField {...args} label="Phone Number" prefix="#" placeholder="(000) 000-0000" /> |
There was a problem hiding this comment.
doesn't it depend what color space and channel the colorfield has? E.g. hsl + hue doesn't have a hash
| */ | ||
| size?: 'S' | 'M' | 'L' | 'XL' | ||
| /** | ||
| * The prefix to display in the text field. Either a string or workflow icon. |
There was a problem hiding this comment.
wonder if an avatar or something should be supported as well
For the moment no, but sounds like in the future, possibly along with swatches |
|
Combobox was also discussed, but it has open questions. I've opened it as a follow up #9999 We can do ColorField there too. |


Closes
Adds prefix (and icon) under a new prop,
prefix. This is all that Spectrum has defined so far. Eventually we may want to open it to something that can handle more, but probably through a very different API.✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project: