diff --git a/packages/@react-aria/utils/src/filterDOMProps.ts b/packages/@react-aria/utils/src/filterDOMProps.ts index 7912334f985..f6a03d1eefa 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,51 @@ const linkPropNames = new Set([ 'referrerPolicy' ]); +const globalAttrs = new Set([ + 'dir', + 'lang', + 'hidden', + 'inert', + 'translate' +]); + +const globalEvents = new Set([ + 'onClick', + '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 +86,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 +103,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 +113,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