From e245cc828157535b5c3f045a988fcef37f9dc410 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Fri, 30 May 2025 13:27:20 -0700 Subject: [PATCH 1/3] chore: Pass through more DOM events and attributes --- .../@react-aria/utils/src/filterDOMProps.ts | 56 ++++++++- packages/@react-types/shared/src/dom.d.ts | 113 +++++++++++++++++- .../DocsTransformer.js | 8 +- .../react-aria-components/src/Breadcrumbs.tsx | 16 ++- packages/react-aria-components/src/Button.tsx | 13 +- .../react-aria-components/src/Calendar.tsx | 45 ++++--- .../react-aria-components/src/Checkbox.tsx | 13 +- .../react-aria-components/src/Collection.tsx | 4 +- .../react-aria-components/src/ColorArea.tsx | 11 +- .../react-aria-components/src/ColorField.tsx | 6 +- .../react-aria-components/src/ColorSlider.tsx | 5 +- .../react-aria-components/src/ColorSwatch.tsx | 9 +- .../src/ColorSwatchPicker.tsx | 6 +- .../react-aria-components/src/ColorThumb.tsx | 6 +- .../react-aria-components/src/ColorWheel.tsx | 9 +- .../react-aria-components/src/ComboBox.tsx | 6 +- .../react-aria-components/src/DateField.tsx | 18 +-- .../react-aria-components/src/DatePicker.tsx | 20 ++-- packages/react-aria-components/src/Dialog.tsx | 12 +- .../react-aria-components/src/Disclosure.tsx | 24 ++-- .../react-aria-components/src/DropZone.tsx | 9 +- .../react-aria-components/src/FieldError.tsx | 6 +- .../react-aria-components/src/FileTrigger.tsx | 5 +- packages/react-aria-components/src/Form.tsx | 3 +- .../react-aria-components/src/GridList.tsx | 21 ++-- packages/react-aria-components/src/Link.tsx | 9 +- .../react-aria-components/src/ListBox.tsx | 31 ++--- packages/react-aria-components/src/Menu.tsx | 27 +++-- packages/react-aria-components/src/Meter.tsx | 9 +- packages/react-aria-components/src/Modal.tsx | 10 +- .../react-aria-components/src/NumberField.tsx | 6 +- .../react-aria-components/src/Popover.tsx | 6 +- .../react-aria-components/src/ProgressBar.tsx | 8 +- .../react-aria-components/src/RadioGroup.tsx | 13 +- .../react-aria-components/src/SearchField.tsx | 5 +- packages/react-aria-components/src/Select.tsx | 12 +- .../react-aria-components/src/Separator.tsx | 10 +- packages/react-aria-components/src/Slider.tsx | 20 ++-- packages/react-aria-components/src/Switch.tsx | 6 +- packages/react-aria-components/src/Table.tsx | 62 +++++----- packages/react-aria-components/src/Tabs.tsx | 30 ++--- .../react-aria-components/src/TagGroup.tsx | 18 +-- .../react-aria-components/src/TextField.tsx | 5 +- packages/react-aria-components/src/Toast.tsx | 18 +-- .../src/ToggleButton.tsx | 11 +- .../src/ToggleButtonGroup.tsx | 10 +- .../react-aria-components/src/Toolbar.tsx | 9 +- .../react-aria-components/src/Tooltip.tsx | 11 +- packages/react-aria-components/src/Tree.tsx | 16 +-- packages/react-aria-components/src/utils.tsx | 7 +- 50 files changed, 507 insertions(+), 306 deletions(-) diff --git a/packages/@react-aria/utils/src/filterDOMProps.ts b/packages/@react-aria/utils/src/filterDOMProps.ts index 7912334f985..b5819d8bd6a 100644 --- a/packages/@react-aria/utils/src/filterDOMProps.ts +++ b/packages/@react-aria/utils/src/filterDOMProps.ts @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, DOMProps, LinkDOMProps} from '@react-types/shared'; +import {AriaLabelingProps, DOMProps, GlobalDOMAttributes, LinkDOMProps} from '@react-types/shared'; const DOMPropNames = new Set([ 'id' @@ -34,6 +34,50 @@ const linkPropNames = new Set([ 'referrerPolicy' ]); +const globalAttrs = new Set([ + 'dir', + 'lang', + 'hidden', + 'inert', + 'translate' +]); + +const globalEvents = new Set([ + 'onAuxClick', + 'onContextMenu', + 'onDoubleClick', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onTouchCancel', + 'onTouchEnd', + 'onTouchMove', + 'onTouchStart', + 'onPointerDown', + 'onPointerMove', + 'onPointerUp', + 'onPointerCancel', + 'onPointerEnter', + 'onPointerLeave', + 'onPointerOver', + 'onPointerOut', + 'onGotPointerCapture', + 'onLostPointerCapture', + 'onScroll', + 'onWheel', + 'onAnimationStart', + 'onAnimationEnd', + 'onAnimationIteration', + 'onTransitionCancel', + 'onTransitionEnd', + 'onTransitionRun', + 'onTransitionStart' +]); + interface Options { /** * If labelling associated aria properties should be included in the filter. @@ -41,6 +85,10 @@ interface Options { labelable?: boolean, /** Whether the element is a link and should include DOM props for elements. */ isLink?: boolean, + /** Whether to include global DOM attributes. */ + global?: boolean, + /** Whether to include DOM events. */ + events?: boolean, /** * A Set of other property names that should be included in the filter. */ @@ -54,8 +102,8 @@ const propRe = /^(data-.*)$/; * @param props - The component props to be filtered. * @param opts - Props to override. */ -export function filterDOMProps(props: DOMProps & AriaLabelingProps & LinkDOMProps, opts: Options = {}): DOMProps & AriaLabelingProps { - let {labelable, isLink, propNames} = opts; +export function filterDOMProps(props: DOMProps & AriaLabelingProps & LinkDOMProps & GlobalDOMAttributes, opts: Options = {}): DOMProps & AriaLabelingProps { + let {labelable, isLink, global, events = global, propNames} = opts; let filteredProps = {}; for (const prop in props) { @@ -64,6 +112,8 @@ export function filterDOMProps(props: DOMProps & AriaLabelingProps & LinkDOMProp DOMPropNames.has(prop) || (labelable && labelablePropNames.has(prop)) || (isLink && linkPropNames.has(prop)) || + (global && globalAttrs.has(prop)) || + (events && globalEvents.has(prop) || (prop.endsWith('Capture') && globalEvents.has(prop.slice(0, -7)))) || propNames?.has(prop) || propRe.test(prop) ) diff --git a/packages/@react-types/shared/src/dom.d.ts b/packages/@react-types/shared/src/dom.d.ts index d6acd30ba68..9e49190f117 100644 --- a/packages/@react-types/shared/src/dom.d.ts +++ b/packages/@react-types/shared/src/dom.d.ts @@ -11,6 +11,7 @@ */ import { + AnimationEventHandler, AriaAttributes, AriaRole, ClipboardEventHandler, @@ -19,8 +20,14 @@ import { FormEventHandler, HTMLAttributeAnchorTarget, HTMLAttributeReferrerPolicy, + MouseEventHandler, + PointerEventHandler, DOMAttributes as ReactDOMAttributes, - ReactEventHandler + ReactEventHandler, + TouchEventHandler, + TransitionEventHandler, + UIEventHandler, + WheelEventHandler } from 'react'; export interface AriaLabelingProps { @@ -223,3 +230,107 @@ export interface DOMAttributes extends AriaAttributes, Rea export interface GroupDOMAttributes extends Omit, 'role'> { role?: 'group' | 'region' | 'presentation' } + +/** + * Global attributes that can be applied to any DOM element. + * @private + */ +// NOTE: id is handled elsewhere (DOMProps). +export interface GlobalDOMAttributes extends GlobalDOMEvents { + dir?: string | undefined, + lang?: string | undefined, + hidden?: boolean | undefined, + inert?: boolean | undefined, + translate?: 'yes' | 'no' | undefined +} + +/** + * Global DOM events that are supported on all DOM elements. + * @private + */ +// NOTES: +// - Drag and drop events are omitted for now. +// - Keyboard and focus events are supported directly on focusable elements (FocusableProps). +// - Text input events (e.g. onInput, onCompositionStart, onCopy) are +// supported only directly on input elements (TextInputDOMProps). +// We don't support contentEditable on our components. +// - Media events should be handled directly on the