diff --git a/packages/ui/src/components/TextField/TextField.tsx b/packages/ui/src/components/TextField/TextField.tsx index 24c038fdb8..88fb006b60 100644 --- a/packages/ui/src/components/TextField/TextField.tsx +++ b/packages/ui/src/components/TextField/TextField.tsx @@ -16,48 +16,36 @@ import { forwardRef, useEffect } from 'react'; import { Input, TextField as AriaTextField } from 'react-aria-components'; -import clsx from 'clsx'; import { FieldLabel } from '../FieldLabel'; import { FieldError } from '../FieldError'; import type { TextFieldProps } from './types'; -import { useStyles } from '../../hooks/useStyles'; +import { useDefinition } from '../../hooks/useDefinition'; import { TextFieldDefinition } from './definition'; -import styles from './TextField.module.css'; /** @public */ export const TextField = forwardRef( (props, ref) => { - const { - label, - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledBy, - } = props; - - useEffect(() => { - if (!label && !ariaLabel && !ariaLabelledBy) { - console.warn( - 'TextField requires either a visible label, aria-label, or aria-labelledby for accessibility', - ); - } - }, [label, ariaLabel, ariaLabelledBy]); - - const { classNames, dataAttributes, style, cleanedProps } = useStyles( + const { ownProps, restProps, dataAttributes } = useDefinition( TextFieldDefinition, - { - size: 'small', - ...props, - }, + props, ); - const { - className, - description, + classes, + label, icon, isRequired, secondaryLabel, placeholder, - ...rest - } = cleanedProps; + description, + } = ownProps; + + useEffect(() => { + if (!label && !restProps['aria-label'] && !restProps['aria-labelledby']) { + console.warn( + 'TextField requires either a visible label, aria-label, or aria-labelledby for accessibility', + ); + } + }, [label, restProps['aria-label'], restProps['aria-labelledby']]); // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. const secondaryLabelText = @@ -65,12 +53,9 @@ export const TextField = forwardRef( return ( ( description={description} />
{icon && ( )} diff --git a/packages/ui/src/components/TextField/definition.ts b/packages/ui/src/components/TextField/definition.ts index c1cab8095d..1524e3ce67 100644 --- a/packages/ui/src/components/TextField/definition.ts +++ b/packages/ui/src/components/TextField/definition.ts @@ -14,13 +14,16 @@ * limitations under the License. */ -import type { ComponentDefinition } from '../../types'; +import { defineComponent } from '../../hooks/useDefinition'; +import type { TextFieldOwnProps } from './types'; +import styles from './TextField.module.css'; /** * Component definition for TextField * @public */ -export const TextFieldDefinition = { +export const TextFieldDefinition = defineComponent()({ + styles, classNames: { root: 'bui-TextField', inputWrapper: 'bui-InputWrapper', @@ -28,9 +31,14 @@ export const TextFieldDefinition = { inputIcon: 'bui-InputIcon', inputAction: 'bui-InputAction', }, - dataAttributes: { - invalid: [true, false] as const, - disabled: [true, false] as const, - size: ['small', 'medium'] as const, + propDefs: { + size: { dataAttribute: true, default: 'small' }, + className: {}, + icon: {}, + placeholder: {}, + label: {}, + description: {}, + secondaryLabel: {}, + isRequired: {}, }, -} as const satisfies ComponentDefinition; +}); diff --git a/packages/ui/src/components/TextField/types.ts b/packages/ui/src/components/TextField/types.ts index 456d6964a0..c436c17937 100644 --- a/packages/ui/src/components/TextField/types.ts +++ b/packages/ui/src/components/TextField/types.ts @@ -15,14 +15,40 @@ */ import type { TextFieldProps as AriaTextFieldProps } from 'react-aria-components'; -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import type { Breakpoint } from '../../types'; import type { FieldLabelProps } from '../FieldLabel/types'; +/** @public */ +export type TextFieldOwnProps = { + /** + * The size of the text field + * @defaultValue 'medium' + */ + size?: 'small' | 'medium' | Partial>; + + className?: string; + + /** + * An icon to render before the input + */ + icon?: ReactNode; + + /** + * Text to display in the input when it has no value + */ + placeholder?: string; + + label?: FieldLabelProps['label']; + description?: FieldLabelProps['description']; + secondaryLabel?: FieldLabelProps['secondaryLabel']; + isRequired?: boolean; +}; + /** @public */ export interface TextFieldProps - extends AriaTextFieldProps, - Omit { + extends Omit, + TextFieldOwnProps { /** * The HTML input type for the text field * @@ -31,19 +57,4 @@ export interface TextFieldProps * search inputs and `PasswordField` for password inputs. */ type?: 'text' | 'email' | 'tel' | 'url'; - /** - * An icon to render before the input - */ - icon?: ReactNode; - - /** - * The size of the text field - * @defaultValue 'medium' - */ - size?: 'small' | 'medium' | Partial>; - - /** - * Text to display in the input when it has no value - */ - placeholder?: string; }