-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: Support onClick as an alias for onPress #7891
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
Thanks for opening up this discussion @devongovett. I would like to first inform myself better with the issues How much of the issues you described in (Building a Button Part 1: Press Events) is still relevant? It's been 5 years since the article was published — I assume browsers had at least fixed some of the inconsistencies mentioned in the article. Or are there as well other sources to check which issues |
Some of it is still relevant, but there have been quite a few improvements. I'm planning on writing an update to that blog post series soon. The major thing that changed recently is that buttons now receive focus consistently in all browsers, so this part is no longer necessary. Therefore, as of #7542 we no longer use Also in that PR, we now trigger However, other behaviors described in the original blog post, such as text selection behavior, active state, hover state, cancelation, virtual clicks, repeating keyboard events, etc. still apply. I believe it's still important to handle those for a good experience, especially on mobile/touch devices, but luckily we can now rely on native events more. |
Looking good @devongovett!
I think your onPress implementation makes a lot of sense for common UI components – onClick would benefit from that work too, and it may not be that much different from native behavior. In your example of filtering only left clicks, browsers do this too, right? I don't trust my memory, so quickly tested this with a mouse on both a
What are the cases? Anything beyond keyboard events?
If I saw a strikethrough, but it worked, though I couldn't find info in the docs, I'd be a little confused. Would it be okay to treat onClick as a sort of "compatibility alias", not a deprecated feature, and the docs explain this while recommending onPress when possible? |
Hi Devon, We're building our company's design system on top of React Aria Components, and I’m really interested in this discussion. I’ve been exploring the idea of aliasing Regarding your questions, I think it makes the most sense for As for event filtering and triggering Lastly, about the In any cases, thank you for considering this API change, and more importantly thank you for your work on RAC |
You're right. onClick is only fired for left clicks. I was thinking of onMouseDown and onMouseUp. The repeating keyboard events (hold down the enter key) are a case where browsers fire repeated clicks and we do not fire repeated onPress events.
Keyboard events are the main one. Another case is iOS which does not fire onClick if you press and hold, unlike other browsers. We normalize this case so that onPress is always fired. |
Thanks for clarifying @devongovett. This all sounds like a desirable upgrade. It's almost like a compatibility fix for onClick, which makes a lot of sense to expose under the same name in a UI kit. Appreciate the pragmatism in offering it! |
I pretty much agree with what @MilllerTime has stated. So |
…onclick # Conflicts: # packages/@react-aria/interactions/src/utils.ts
Thanks all for the feedback! We're going to move ahead with this. I've removed |
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.
LGTM
## API Changes
react-aria-components/react-aria-components:Button Button {
aria-controls?: string
aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode | ((ButtonRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((ButtonRenderProps & {
defaultClassName: string | undefined
})) => string
excludeFromTabOrder?: boolean
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isPending?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
slot?: string | null
style?: CSSProperties | ((ButtonRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
} /react-aria-components:Link Link {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode | ((LinkRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((LinkRenderProps & {
defaultClassName: string | undefined
})) => string
download?: boolean | string
href?: Href
hrefLang?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
slot?: string | null
style?: CSSProperties | ((LinkRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
target?: HTMLAttributeAnchorTarget
} /react-aria-components:ToggleButton ToggleButton {
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode | ((ToggleButtonRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((ToggleButtonRenderProps & {
defaultClassName: string | undefined
})) => string
defaultSelected?: boolean
excludeFromTabOrder?: boolean
id?: Key
isDisabled?: boolean
isSelected?: boolean
onBlur?: (FocusEvent<Target>) => void
onChange?: (boolean) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
slot?: string | null
style?: CSSProperties | ((ToggleButtonRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
type?: 'button' | 'submit' | 'reset' = 'button'
} /react-aria-components:Pressable Pressable {
allowTextSelectionOnPress?: boolean
children: ReactElement<DOMAttributes, string>
isDisabled?: boolean
isPressed?: boolean
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
preventFocusOnPress?: boolean
shouldCancelOnPointerExit?: boolean
} /react-aria-components:ButtonProps ButtonProps {
aria-controls?: string
aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode | ((ButtonRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((ButtonRenderProps & {
defaultClassName: string | undefined
})) => string
excludeFromTabOrder?: boolean
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isPending?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
slot?: string | null
style?: CSSProperties | ((ButtonRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
} /react-aria-components:LinkProps LinkProps {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode | ((LinkRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((LinkRenderProps & {
defaultClassName: string | undefined
})) => string
download?: boolean | string
href?: Href
hrefLang?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
slot?: string | null
style?: CSSProperties | ((LinkRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
target?: HTMLAttributeAnchorTarget
} /react-aria-components:ToggleButtonProps ToggleButtonProps {
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode | ((ToggleButtonRenderProps & {
defaultChildren: ReactNode | undefined
})) => ReactNode
className?: string | ((ToggleButtonRenderProps & {
defaultClassName: string | undefined
})) => string
defaultSelected?: boolean
excludeFromTabOrder?: boolean
id?: Key
isDisabled?: boolean
isSelected?: boolean
onBlur?: (FocusEvent<Target>) => void
onChange?: (boolean) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
slot?: string | null
style?: CSSProperties | ((ToggleButtonRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
type?: 'button' | 'submit' | 'reset' = 'button'
} @react-aria/breadcrumbs/@react-aria/breadcrumbs:AriaBreadcrumbItemProps AriaBreadcrumbItemProps {
aria-current?: 'page' | 'step' | 'location' | 'date' | 'time' | boolean | 'true' | 'false' = 'page'
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children: ReactNode
download?: boolean | string
elementType?: string = 'a'
href?: Href
hrefLang?: string
id?: string
isCurrent?: boolean
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
target?: HTMLAttributeAnchorTarget
} @react-aria/button/@react-aria/button:AriaButtonOptions AriaButtonOptions <E extends ElementType> {
aria-controls?: string
aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
elementType?: ElementType | JSXElementConstructor<any> = 'button'
excludeFromTabOrder?: boolean
href?: string
id?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
rel?: string
target?: string
type?: 'button' | 'submit' | 'reset' = 'button'
} /@react-aria/button:AriaButtonProps AriaButtonProps <T extends ElementType = 'button'> {
aria-controls?: string
aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
elementType?: ElementType | JSXElementConstructor<any> = 'button'
excludeFromTabOrder?: boolean
href?: string
id?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
rel?: string
target?: string
type?: 'button' | 'submit' | 'reset' = 'button'
} /@react-aria/button:AriaToggleButtonProps AriaToggleButtonProps <T extends ElementType = 'button'> {
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
defaultSelected?: boolean
elementType?: ElementType | JSXElementConstructor<any> = 'button'
excludeFromTabOrder?: boolean
id?: string
isDisabled?: boolean
isSelected?: boolean
onBlur?: (FocusEvent<Target>) => void
onChange?: (boolean) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
type?: 'button' | 'submit' | 'reset' = 'button'
} /@react-aria/button:AriaToggleButtonGroupItemProps AriaToggleButtonGroupItemProps <E extends ElementType = 'button'> {
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
elementType?: ElementType | JSXElementConstructor<any> = 'button'
excludeFromTabOrder?: boolean
id: Key
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
type?: 'button' | 'submit' | 'reset' = 'button'
} @react-aria/dnd/@react-aria/dnd:DropResult DropResult {
dropButtonProps?: AriaButtonProps
- dropProps: HTMLAttributes<HTMLElement>
+ dropProps: DOMAttributes
isDropTarget: boolean
} @react-aria/interactions/@react-aria/interactions:Pressable Pressable {
allowTextSelectionOnPress?: boolean
children: ReactElement<DOMAttributes, string>
isDisabled?: boolean
isPressed?: boolean
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
preventFocusOnPress?: boolean
shouldCancelOnPointerExit?: boolean
} /@react-aria/interactions:PressResponder PressResponder {
allowTextSelectionOnPress?: boolean
children: ReactNode
isDisabled?: boolean
isPressed?: boolean
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
preventFocusOnPress?: boolean
shouldCancelOnPointerExit?: boolean
} /@react-aria/interactions:PressProps PressProps {
allowTextSelectionOnPress?: boolean
isDisabled?: boolean
isPressed?: boolean
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
preventFocusOnPress?: boolean
shouldCancelOnPointerExit?: boolean
} /@react-aria/interactions:PressHookProps PressHookProps {
allowTextSelectionOnPress?: boolean
isDisabled?: boolean
isPressed?: boolean
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
preventFocusOnPress?: boolean
ref?: RefObject<Element | null>
shouldCancelOnPointerExit?: boolean
} /@react-aria/interactions:PressEvents PressEvents {
+ onClick?: (MouseEvent<FocusableElement>) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
} @react-aria/link/@react-aria/link:AriaLinkOptions AriaLinkOptions {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
download?: boolean | string
elementType?: string = 'a'
href?: Href
hrefLang?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
target?: HTMLAttributeAnchorTarget
} @react-aria/menu/@react-aria/menu:AriaMenuItemProps AriaMenuItemProps {
aria-controls?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: 'menu' | 'dialog'
aria-label?: string
closeOnSelect?: boolean = true
id?: string
isVirtualized?: boolean
key: Key
onBlur?: (FocusEvent<Target>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
selectionManager?: SelectionManager
} @react-spectrum/textfield/@react-spectrum/textfield:TextFieldBase TextFieldBase extends Partial {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
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>
contextualHelp?: ReactNode
defaultValue?: string
description?: ReactNode
descriptionProps?: HTMLAttributes<HTMLElement>
disableFocusRing?: boolean
end?: Responsive<DimensionValue>
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
errorMessage?: ReactNode | (ValidationResult) => ReactNode
errorMessageProps?: HTMLAttributes<HTMLElement>
excludeFromTabOrder?: boolean
flex?: Responsive<string | number | boolean>
flexBasis?: Responsive<number | string>
flexGrow?: Responsive<number>
flexShrink?: Responsive<number>
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
inputClassName?: string
inputMode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
inputProps: InputHTMLAttributes<HTMLInputElement> | TextareaHTMLAttributes<HTMLTextAreaElement>
inputRef?: RefObject<HTMLInputElement | HTMLTextAreaElement | null>
isDisabled?: boolean
isHidden?: Responsive<boolean>
isLoading?: boolean
isQuiet?: boolean
isReadOnly?: boolean
isRequired?: boolean
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'
labelProps?: LabelHTMLAttributes<HTMLLabelElement>
left?: Responsive<DimensionValue>
loadingIndicator?: ReactElement
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>
minHeight?: Responsive<DimensionValue>
minLength?: number
minWidth?: Responsive<DimensionValue>
multiLine?: boolean
name?: string
necessityIndicator?: NecessityIndicator = 'icon'
onBeforeInput?: FormEventHandler<HTMLInputElement>
onBlur?: (FocusEvent<T>) => void
+ onClick?: (MouseEvent<FocusableElement>) => void
onCompositionEnd?: CompositionEventHandler<HTMLInputElement>
onCompositionStart?: CompositionEventHandler<HTMLInputElement>
onCompositionUpdate?: CompositionEventHandler<HTMLInputElement>
onCopy?: ClipboardEventHandler<HTMLInputElement>
onFocus?: (FocusEvent<T>) => void
onFocusChange?: (boolean) => void
onInput?: FormEventHandler<HTMLInputElement>
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPaste?: ClipboardEventHandler<HTMLInputElement>
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
onSelect?: ReactEventHandler<HTMLInputElement>
order?: Responsive<number>
pattern?: string
position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
right?: Responsive<DimensionValue>
spellCheck?: string
start?: Responsive<DimensionValue>
top?: Responsive<DimensionValue>
type?: 'text' | 'search' | 'url' | 'tel' | 'email' | 'password' | (string & {
})
validationBehavior?: 'aria' | 'native' = 'aria'
validationIconClassName?: string
validationState?: ValidationState
value?: string
width?: Responsive<DimensionValue>
wrapperChildren?: ReactElement | Array<ReactElement>
zIndex?: Responsive<number>
} |
Proposal for discussion. See this discussion: https://x.com/deebovv/status/1897355956188537016
In particular:
We solved the second case with
<Pressable>
added in the last release. The first case of using a RAC Button as a trigger for a third-party component that passes inonClick
remains.This PR basically treats
onClick
as an alias foronPress
. It gets fired at the same times. If we have a nativeMouseEvent
event available, it gets passed to the handler. If not, e.g. in a keyboard event, we synthesize a fake click event and pass that. This matches what browsers do for certain elements: https://html.spec.whatwg.org/#fire-a-synthetic-pointer-event.Questions:
usePress
to avoid edge cases. For example, we only fireonPress
for left clicks, not right clicks, and we don't fireonPress
for repeating keyboard events. Seems like matching this inonClick
would be good to take advantage of this behavior, but could be unexpected that it doesn't match the browser behavior foronClick
. But if people will be usingonClick
just because they're used to that name, they'd lose out on some of this improved behavior, so I lean towards having it matchonPress
. Thoughts?onClick
in cases where the browser doesn't do so?onClick
as@deprecated
in the prop definition, which means it'll be omitted from the docs. This way we nudge people towardonPress
but it works regardless, e.g. when used with third party libraries. Also means it'll be rendered with a strikethrough in people's IDEs. But, there's no more console warning. Thoughts?