mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-29 08:14:57 +00:00
FakeMUI Components (MUI API compatibility): - Add sx prop support to all components via sxToStyle utility - Add MUI-style variants to Button (contained, outlined) - Add component prop to Typography for polymorphic rendering - Add label prop to Chip (MUI uses label vs children) - Add edge/color/size props to IconButton - Add component prop to List for nav rendering - Add href support to ListItemButton - Add variant prop to Avatar - Add PaperProps to Drawer New @metabuilder/components package: - vanilla/loading - LoadingIndicator, InlineLoader, AsyncLoading - vanilla/error - ErrorBoundary, ErrorDisplay, withErrorBoundary - vanilla/empty-state - EmptyState + 7 specialized variants - vanilla/skeleton - Skeleton + 6 specialized variants - Organized by framework: vanilla/, radix/, fakemui/ Hooks consolidation (FakeMUI → root hooks/): - useAccessible (5 accessibility hooks) - useToast with ToastProvider - FakeMUI re-exports from hooks for backward compatibility WorkflowUI fixes: - Fix showNotification → useUI error/success methods - Fix Redux reducer setTimeout (Immer proxy issue) - Fix useRef type error - Update to Next.js 16.1.6 with webpack mode - Add @metabuilder/fakemui dependency Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import React, { forwardRef, useId } from 'react'
|
|
import { FormLabel } from './FormLabel'
|
|
import { FormHelperText } from './FormHelperText'
|
|
import { Input, InputProps } from './Input'
|
|
import { Select } from './Select'
|
|
import { useAccessible } from '../../../src/utils/useAccessible'
|
|
import { sxToStyle } from '../utils/sx'
|
|
|
|
export interface TextFieldProps extends Omit<InputProps, 'size' | 'label' | 'helperText'> {
|
|
label?: React.ReactNode
|
|
helperText?: React.ReactNode
|
|
error?: boolean
|
|
/** Render as a select dropdown instead of input */
|
|
select?: boolean
|
|
/** Children (for select mode - MenuItem components) */
|
|
children?: React.ReactNode
|
|
/** Input size */
|
|
size?: 'small' | 'medium'
|
|
/** Unique identifier for testing and accessibility */
|
|
testId?: string
|
|
/** MUI sx prop for styling */
|
|
sx?: Record<string, unknown>
|
|
}
|
|
|
|
export const TextField = forwardRef<HTMLInputElement | HTMLSelectElement, TextFieldProps>(
|
|
({ label, helperText, error, className = '', id: providedId, select, children, size, testId: customTestId, sx, style, ...props }, ref) => {
|
|
const generatedId = useId()
|
|
const id = providedId ?? generatedId
|
|
const helperTextId = `${id}-helper-text`
|
|
|
|
// Convert size prop to Input's expected format
|
|
const inputSize = size === 'small' ? 'sm' : size === 'medium' ? 'md' : undefined
|
|
|
|
const accessible = useAccessible({
|
|
feature: 'form',
|
|
component: select ? 'select' : 'input',
|
|
identifier: customTestId || String(label)?.substring(0, 20),
|
|
ariaDescribedBy: helperText ? helperTextId : undefined,
|
|
})
|
|
|
|
return (
|
|
<div className={`text-field ${error ? 'text-field--error' : ''} ${className}`} style={{ ...sxToStyle(sx), ...style }}>
|
|
{label && <FormLabel htmlFor={id}>{label}</FormLabel>}
|
|
{select ? (
|
|
<Select
|
|
ref={ref as React.Ref<HTMLSelectElement>}
|
|
id={id}
|
|
error={error}
|
|
className="select--full-width"
|
|
data-testid={accessible['data-testid']}
|
|
aria-invalid={error}
|
|
aria-describedby={helperText ? helperTextId : undefined}
|
|
{...(props as unknown as React.SelectHTMLAttributes<HTMLSelectElement>)}
|
|
>
|
|
{children}
|
|
</Select>
|
|
) : (
|
|
<Input
|
|
ref={ref as React.Ref<HTMLInputElement>}
|
|
id={id}
|
|
error={error}
|
|
size={inputSize}
|
|
data-testid={accessible['data-testid']}
|
|
aria-invalid={error}
|
|
aria-describedby={helperText ? helperTextId : undefined}
|
|
{...props}
|
|
/>
|
|
)}
|
|
{helperText && (
|
|
<FormHelperText error={error} id={helperTextId} role="status">
|
|
{helperText}
|
|
</FormHelperText>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
)
|
|
|
|
TextField.displayName = 'TextField'
|