ui: add more props to TextInput

pull/305/head
Yangshun Tay 2 years ago
parent e93cc73d51
commit d68018b755

@ -14,12 +14,12 @@ export default {
autoComplete: { autoComplete: {
control: 'text', control: 'text',
}, },
disabled: {
control: 'boolean',
},
errorMessage: { errorMessage: {
control: 'text', control: 'text',
}, },
isDisabled: {
control: 'boolean',
},
isLabelHidden: { isLabelHidden: {
control: 'boolean', control: 'boolean',
}, },
@ -103,7 +103,7 @@ export function Icon() {
export function Disabled() { export function Disabled() {
return ( return (
<TextInput <TextInput
isDisabled={true} disabled={true}
label="Disabled input" label="Disabled input"
placeholder="John Doe" placeholder="John Doe"
type="text" type="text"

@ -1,23 +1,46 @@
import clsx from 'clsx'; import clsx from 'clsx';
import type { ChangeEvent } from 'react'; import type {
import React, { useId } from 'react'; ChangeEvent,
FocusEvent,
ForwardedRef,
InputHTMLAttributes,
} from 'react';
import React, { forwardRef, useId } from 'react';
type TextInputInputAttributes = Pick<
InputHTMLAttributes<HTMLInputElement>,
| 'autoComplete'
| 'disabled'
| 'max'
| 'maxLength'
| 'min'
| 'minLength'
| 'name'
| 'pattern'
| 'placeholder'
| 'required'
| 'type'
>;
type TextInputDOMAttributes = Pick<
InputHTMLAttributes<HTMLInputElement>,
'onBlur' | 'onFocus'
>;
type Props = Readonly<{ type Props = Readonly<{
autoComplete?: string;
defaultValue?: string; defaultValue?: string;
endIcon?: React.ComponentType<React.ComponentProps<'svg'>>; endIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
errorMessage?: React.ReactNode; errorMessage?: React.ReactNode;
id?: string; id?: string;
isDisabled?: boolean;
isLabelHidden?: boolean; isLabelHidden?: boolean;
label: string; label: string;
name?: string; onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void; onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
placeholder?: string;
startIcon?: React.ComponentType<React.ComponentProps<'svg'>>; startIcon?: React.ComponentType<React.ComponentProps<'svg'>>;
type?: 'email' | 'password' | 'text';
value?: string; value?: string;
}>; }> &
Readonly<TextInputDOMAttributes> &
Readonly<TextInputInputAttributes>;
type State = 'error' | 'normal'; type State = 'error' | 'normal';
@ -28,22 +51,23 @@ const stateClasses: Record<State, string> = {
'placeholder:text-slate-400 focus:ring-primary-500 focus:border-primary-500 border-slate-300', 'placeholder:text-slate-400 focus:ring-primary-500 focus:border-primary-500 border-slate-300',
}; };
export default function TextInput({ function TextInput(
autoComplete, {
defaultValue, defaultValue,
disabled,
endIcon: EndIcon, endIcon: EndIcon,
errorMessage, errorMessage,
id: idParam, id: idParam,
isDisabled,
isLabelHidden = false, isLabelHidden = false,
label, label,
name,
placeholder,
startIcon: StartIcon, startIcon: StartIcon,
type = 'text', type = 'text',
value, value,
onChange, onChange,
}: Props) { ...props
}: Props,
ref: ForwardedRef<HTMLInputElement>,
) {
const hasError = errorMessage != null; const hasError = errorMessage != null;
const generatedId = useId(); const generatedId = useId();
const id = idParam ?? generatedId; const id = idParam ?? generatedId;
@ -68,21 +92,19 @@ export default function TextInput({
</div> </div>
)} )}
<input <input
ref={ref}
aria-describedby={hasError ? errorId : undefined} aria-describedby={hasError ? errorId : undefined}
aria-invalid={hasError ? true : undefined} aria-invalid={hasError ? true : undefined}
autoComplete={autoComplete}
className={clsx( className={clsx(
'block w-full rounded-md sm:text-sm', 'block w-full rounded-md sm:text-sm',
StartIcon && 'pl-10', StartIcon && 'pl-10',
EndIcon && 'pr-10', EndIcon && 'pr-10',
stateClasses[state], stateClasses[state],
isDisabled && 'bg-slate-100', disabled && 'bg-slate-100',
)} )}
defaultValue={defaultValue} defaultValue={defaultValue}
disabled={isDisabled} disabled={disabled}
id={id} id={id}
name={name}
placeholder={placeholder}
type={type} type={type}
value={value != null ? value : undefined} value={value != null ? value : undefined}
onChange={(event) => { onChange={(event) => {
@ -92,6 +114,7 @@ export default function TextInput({
onChange(event.target.value, event); onChange(event.target.value, event);
}} }}
{...props}
/> />
{EndIcon && ( {EndIcon && (
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3"> <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
@ -107,3 +130,5 @@ export default function TextInput({
</div> </div>
); );
} }
export default forwardRef(TextInput);

Loading…
Cancel
Save