Add input validation state

This commit is contained in:
Arnaud 2024-10-14 20:36:12 +02:00
parent 887f27be70
commit 411b76160c
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
3 changed files with 54 additions and 88 deletions

View File

@ -3,7 +3,8 @@ import {
ComponentType,
CSSProperties,
forwardRef,
KeyboardEvent,
InputHTMLAttributes,
useState,
} from "react";
import { attributes } from "../utils/attributes";
import { classnames } from "../utils/classnames";
@ -20,74 +21,33 @@ export interface InputCustomStyleCSS extends CSSProperties {
}
type Props = {
label?: string;
id: string;
/**
* OnChange event triggered every time the input value changed.
*/
onChange?: (e: ChangeEvent<HTMLInputElement>) => void | Promise<void>;
onFocus?: () => void | Promise<void>;
onBlur?: () => unknown | Promise<unknown>;
onClick?: (() => void) | undefined;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onKeyUp?: (e: KeyboardEvent<HTMLInputElement>) => void;
placeholder?: string;
value?: string;
/**
* Apply custom css variables.
* --codex-input-background
* --codex-color
* --codex-border-radius
* --codex-input-border
* --codex-color-primary
* --codex-input-background-disabled
*/
style?: InputCustomStyleCSS;
label?: string;
/**
* Helper text to add indication about your input.
*/
helper?: string;
disabled?: boolean;
/**
* Add an icon on the left.
*/
Icon?: ComponentType;
/**
* If the mode is "auto", the component will check the invalid state
* on change and add an invalid state if it is invalid.
*/
mode?: "auto";
isInvalid?: boolean;
/**
* Apply a class to the input element
*/
inputClassName?: string;
/**
* Default is text
*/
type?: string;
step?: string;
name?: string;
min?: number | string;
max?: number | string;
maxLength?: number;
};
} & InputHTMLAttributes<HTMLInputElement>;
export const Input = forwardRef<HTMLInputElement, Props>(
(
@ -95,28 +55,29 @@ export const Input = forwardRef<HTMLInputElement, Props>(
id,
label,
helper,
disabled,
value,
onBlur,
onFocus,
placeholder,
onChange,
onMouseEnter,
onMouseLeave,
onClick,
onKeyUp,
style,
Icon,
step,
name,
inputClassName,
maxLength,
type = "text",
min,
max,
disabled = false,
onChange,
mode,
isInvalid = false,
...rest
},
ref
) => {
const [invalid, setInvalid] = useState(isInvalid);
const onInternalChange = (e: ChangeEvent<HTMLInputElement>) => {
if (mode === "auto") {
setInvalid(e.currentTarget.checkValidity() !== true);
}
onChange?.(e);
};
console.info(rest);
return (
<>
{label && (
@ -133,38 +94,29 @@ export const Input = forwardRef<HTMLInputElement, Props>(
)}
<input
id={id}
name={name}
ref={ref}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
className={classnames(
["input"],
["input--invalid", invalid || isInvalid],
["input-icon-input", !!Icon],
[inputClassName || ""]
)}
onChange={onInternalChange}
style={style}
{...attributes({
disabled,
"aria-disabled": disabled,
})}
value={value}
placeholder={placeholder}
onBlur={onBlur}
onFocus={onFocus}
onChange={onChange}
onKeyUp={onKeyUp}
type={type}
step={step}
min={min}
max={max}
maxLength={maxLength}
{...rest}
/>
</div>
{helper && (
<div>
<SimpleText className="input-helper-text" variant="light">
<SimpleText
className="input-helper-text"
variant={invalid || isInvalid ? "error" : "light"}
>
{helper}
</SimpleText>
</div>

View File

@ -14,6 +14,11 @@
flex-direction: column;
}
.input--invalid {
color: rgb(var(--codex-color-error));
border-color: rgb(var(--codex-color-error));
}
.input-label {
margin-bottom: 0.5rem;
font-weight: 500;

View File

@ -54,10 +54,19 @@ export const Disabled: Story = {
},
};
export const CustomStyle: Story = {
export const AutoInvalid: Story = {
args: {
id: "custom",
label: "Label",
style: { "--codex-input-border": "1px solid red" },
id: "autoinvalid",
label: "Auto invalid",
pattern: "a",
mode: "auto"
},
};
export const IsInvalid: Story = {
args: {
id: "autoinvalid",
label: "Auto invalid",
isInvalid: true
},
};