Add input validation state
This commit is contained in:
parent
887f27be70
commit
411b76160c
|
@ -3,7 +3,8 @@ import {
|
||||||
ComponentType,
|
ComponentType,
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
KeyboardEvent,
|
InputHTMLAttributes,
|
||||||
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { attributes } from "../utils/attributes";
|
import { attributes } from "../utils/attributes";
|
||||||
import { classnames } from "../utils/classnames";
|
import { classnames } from "../utils/classnames";
|
||||||
|
@ -20,74 +21,33 @@ export interface InputCustomStyleCSS extends CSSProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label?: string;
|
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
/**
|
label?: 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper text to add indication about your input.
|
* Helper text to add indication about your input.
|
||||||
*/
|
*/
|
||||||
helper?: string;
|
helper?: string;
|
||||||
|
|
||||||
disabled?: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an icon on the left.
|
* Add an icon on the left.
|
||||||
*/
|
*/
|
||||||
Icon?: ComponentType;
|
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
|
* Apply a class to the input element
|
||||||
*/
|
*/
|
||||||
inputClassName?: string;
|
inputClassName?: string;
|
||||||
|
} & InputHTMLAttributes<HTMLInputElement>;
|
||||||
/**
|
|
||||||
* Default is text
|
|
||||||
*/
|
|
||||||
type?: string;
|
|
||||||
|
|
||||||
step?: string;
|
|
||||||
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
min?: number | string;
|
|
||||||
|
|
||||||
max?: number | string;
|
|
||||||
|
|
||||||
maxLength?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Input = forwardRef<HTMLInputElement, Props>(
|
export const Input = forwardRef<HTMLInputElement, Props>(
|
||||||
(
|
(
|
||||||
|
@ -95,28 +55,29 @@ export const Input = forwardRef<HTMLInputElement, Props>(
|
||||||
id,
|
id,
|
||||||
label,
|
label,
|
||||||
helper,
|
helper,
|
||||||
disabled,
|
|
||||||
value,
|
|
||||||
onBlur,
|
|
||||||
onFocus,
|
|
||||||
placeholder,
|
|
||||||
onChange,
|
|
||||||
onMouseEnter,
|
|
||||||
onMouseLeave,
|
|
||||||
onClick,
|
|
||||||
onKeyUp,
|
|
||||||
style,
|
style,
|
||||||
Icon,
|
Icon,
|
||||||
step,
|
|
||||||
name,
|
|
||||||
inputClassName,
|
inputClassName,
|
||||||
maxLength,
|
disabled = false,
|
||||||
type = "text",
|
onChange,
|
||||||
min,
|
mode,
|
||||||
max,
|
isInvalid = false,
|
||||||
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{label && (
|
{label && (
|
||||||
|
@ -133,38 +94,29 @@ export const Input = forwardRef<HTMLInputElement, Props>(
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
id={id}
|
id={id}
|
||||||
name={name}
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={onClick}
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
className={classnames(
|
className={classnames(
|
||||||
["input"],
|
["input"],
|
||||||
|
["input--invalid", invalid || isInvalid],
|
||||||
["input-icon-input", !!Icon],
|
["input-icon-input", !!Icon],
|
||||||
[inputClassName || ""]
|
[inputClassName || ""]
|
||||||
)}
|
)}
|
||||||
|
onChange={onInternalChange}
|
||||||
style={style}
|
style={style}
|
||||||
{...attributes({
|
{...attributes({
|
||||||
disabled,
|
disabled,
|
||||||
"aria-disabled": disabled,
|
"aria-disabled": disabled,
|
||||||
})}
|
})}
|
||||||
value={value}
|
{...rest}
|
||||||
placeholder={placeholder}
|
|
||||||
onBlur={onBlur}
|
|
||||||
onFocus={onFocus}
|
|
||||||
onChange={onChange}
|
|
||||||
onKeyUp={onKeyUp}
|
|
||||||
type={type}
|
|
||||||
step={step}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
maxLength={maxLength}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{helper && (
|
{helper && (
|
||||||
<div>
|
<div>
|
||||||
<SimpleText className="input-helper-text" variant="light">
|
<SimpleText
|
||||||
|
className="input-helper-text"
|
||||||
|
variant={invalid || isInvalid ? "error" : "light"}
|
||||||
|
>
|
||||||
{helper}
|
{helper}
|
||||||
</SimpleText>
|
</SimpleText>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input--invalid {
|
||||||
|
color: rgb(var(--codex-color-error));
|
||||||
|
border-color: rgb(var(--codex-color-error));
|
||||||
|
}
|
||||||
|
|
||||||
.input-label {
|
.input-label {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
|
@ -54,10 +54,19 @@ export const Disabled: Story = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomStyle: Story = {
|
export const AutoInvalid: Story = {
|
||||||
args: {
|
args: {
|
||||||
id: "custom",
|
id: "autoinvalid",
|
||||||
label: "Label",
|
label: "Auto invalid",
|
||||||
style: { "--codex-input-border": "1px solid red" },
|
pattern: "a",
|
||||||
|
mode: "auto"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IsInvalid: Story = {
|
||||||
|
args: {
|
||||||
|
id: "autoinvalid",
|
||||||
|
label: "Auto invalid",
|
||||||
|
isInvalid: true
|
||||||
},
|
},
|
||||||
};
|
};
|
Loading…
Reference in New Issue