diff --git a/packages/react/src/Radio/Radio.test.tsx b/packages/react/src/Radio/Radio.test.tsx index 1a7b9756dcd..d20071927db 100644 --- a/packages/react/src/Radio/Radio.test.tsx +++ b/packages/react/src/Radio/Radio.test.tsx @@ -21,6 +21,7 @@ describe('Radio', () => { const radio = getByRole('radio') expect(radio).toBeDefined() + expect(radio).toHaveAttribute('type', 'radio') }) it('renders data-component attribute', () => { diff --git a/packages/react/src/Radio/Radio.tsx b/packages/react/src/Radio/Radio.tsx index 5ea79439a9f..d14e073f96e 100644 --- a/packages/react/src/Radio/Radio.tsx +++ b/packages/react/src/Radio/Radio.tsx @@ -5,6 +5,7 @@ import {clsx} from 'clsx' import sharedClasses from '../Checkbox/shared.module.css' import classes from './Radio.module.css' import type {WithSlotMarker} from '../utils/types' +import {mergeProps} from '../utils/mergeProps' export type RadioProps = { /** @@ -32,7 +33,7 @@ export type RadioProps = { * Indicates whether the radio button must be checked before the form can be submitted */ required?: boolean -} & InputHTMLAttributes +} & Omit, 'type'> /** * An accessible, native radio component for selecting one option from a list. @@ -56,7 +57,6 @@ const Radio = React.forwardRef( const radioGroupContext = useContext(RadioGroupContext) const handleOnChange: ChangeEventHandler = e => { radioGroupContext?.onChange && radioGroupContext.onChange(e) - onChange && onChange(e) } const name = nameProp || radioGroupContext?.name @@ -69,17 +69,25 @@ const Radio = React.forwardRef( return ( ) diff --git a/packages/react/src/Radio/Radio.types.test.tsx b/packages/react/src/Radio/Radio.types.test.tsx new file mode 100644 index 00000000000..a08856d1c1e --- /dev/null +++ b/packages/react/src/Radio/Radio.types.test.tsx @@ -0,0 +1,6 @@ +import Radio from '.' + +export function doesNotAcceptType() { + // @ts-expect-error Radio always renders an input with type="radio" + return +} diff --git a/packages/react/src/Token/TokenBase.tsx b/packages/react/src/Token/TokenBase.tsx index 03202c6f0cb..17025fc667f 100644 --- a/packages/react/src/Token/TokenBase.tsx +++ b/packages/react/src/Token/TokenBase.tsx @@ -1,10 +1,10 @@ import type {KeyboardEvent} from 'react' import React from 'react' -import {clsx} from 'clsx' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import classes from './TokenBase.module.css' import {defaultTokenSize, type TokenSizeKeys} from './constants' import {isTokenInteractive} from './utils' +import {mergeProps} from '../utils/mergeProps' export type {TokenSizeKeys} @@ -55,30 +55,38 @@ const TokenBase = React.forwardRef { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.key === 'Backspace' || event.key === 'Delete') && onRemove) { + onRemove() + } + } + return ( ) => { - onKeyDown && onKeyDown(event) - - if ((event.key === 'Backspace' || event.key === 'Delete') && onRemove) { - onRemove() - } - }} - className={clsx(classes.TokenBase, className)} - data-cursor-is-interactive={isTokenInteractive({ - as: Component, - onClick: rest.onClick, - onFocus: rest.onFocus, - tabIndex: rest.tabIndex, - disabled: rest.disabled, - })} - data-size={size} - id={id?.toString()} - {...(Component === 'button' - ? (rest as React.ButtonHTMLAttributes) - : Component === 'a' - ? (rest as React.AnchorHTMLAttributes) - : (rest as React.HTMLAttributes))} + {...mergeProps( + { + onKeyDown: handleKeyDown, + className: classes.TokenBase, + 'data-cursor-is-interactive': isTokenInteractive({ + as: Component, + onClick: rest.onClick, + onFocus: rest.onFocus, + tabIndex: rest.tabIndex, + disabled: rest.disabled, + }), + 'data-size': size, + }, + { + ...(Component === 'button' + ? (rest as React.ButtonHTMLAttributes) + : Component === 'a' + ? (rest as React.AnchorHTMLAttributes) + : (rest as React.HTMLAttributes)), + ...(onKeyDown ? {onKeyDown} : {}), + className, + id: id?.toString(), + }, + )} // TypeScript cannot resolve polymorphic ref types at compile time // This assertion is safe because the ref will match the actual rendered element // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/react/src/Token/__tests__/Token.test.tsx b/packages/react/src/Token/__tests__/Token.test.tsx index 73c01124c2a..ac07bbe22fe 100644 --- a/packages/react/src/Token/__tests__/Token.test.tsx +++ b/packages/react/src/Token/__tests__/Token.test.tsx @@ -63,6 +63,17 @@ const testTokenComponent = (Component: React.ComponentType { + const calls: string[] = [] + const {getByText} = HTMLRender( + calls.push('remove')} onKeyDown={() => calls.push('consumer')} />, + ) + + fireEvent.keyDown(getByText('token'), {key: 'Backspace'}) + + expect(calls).toEqual(['remove', 'consumer']) + }) + it('adds className to rendered component', () => { const {getByText} = HTMLRender() const domNode = getByText('token') diff --git a/packages/react/src/VisuallyHidden/VisuallyHidden.tsx b/packages/react/src/VisuallyHidden/VisuallyHidden.tsx index d05c496352f..d2b8231d952 100644 --- a/packages/react/src/VisuallyHidden/VisuallyHidden.tsx +++ b/packages/react/src/VisuallyHidden/VisuallyHidden.tsx @@ -1,6 +1,6 @@ -import {clsx} from 'clsx' import type React from 'react' import {type HTMLAttributes} from 'react' +import {mergeProps} from '../utils/mergeProps' import classes from './VisuallyHidden.module.css' /** @@ -13,12 +13,8 @@ import classes from './VisuallyHidden.module.css' * * @see https://www.scottohara.me/blog/2023/03/21/visually-hidden-hack.html */ -export const VisuallyHidden = ({className, children, ...rest}: VisuallyHiddenProps) => { - return ( - - {children} - - ) +export const VisuallyHidden = ({children, ...rest}: VisuallyHiddenProps) => { + return {children} } export type VisuallyHiddenProps = React.PropsWithChildren<