diff --git a/apps/docs/content/components/input/built-in-validation.raw.jsx b/apps/docs/content/components/input/built-in-validation.raw.jsx new file mode 100644 index 0000000000..70ca6412fa --- /dev/null +++ b/apps/docs/content/components/input/built-in-validation.raw.jsx @@ -0,0 +1,40 @@ +import {Button, Form, Input} from "@nextui-org/react"; + +export default function App() { + const [submitted, setSubmitted] = React.useState(null); + + const onSubmit = (e) => { + e.preventDefault(); + const data = Object.fromEntries(new FormData(e.currentTarget)); + + setSubmitted(data); + }; + + return ( +
+ ); +} diff --git a/apps/docs/content/components/input/built-in-validation.ts b/apps/docs/content/components/input/built-in-validation.ts new file mode 100644 index 0000000000..407bbc6a41 --- /dev/null +++ b/apps/docs/content/components/input/built-in-validation.ts @@ -0,0 +1,9 @@ +import App from "./built-in-validation.raw.jsx?raw"; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/input/custom-validation.raw.jsx b/apps/docs/content/components/input/custom-validation.raw.jsx new file mode 100644 index 0000000000..78525f9f58 --- /dev/null +++ b/apps/docs/content/components/input/custom-validation.raw.jsx @@ -0,0 +1,40 @@ +import {Button, Form, Input} from "@nextui-org/react"; + +export default function App() { + const [submitted, setSubmitted] = React.useState(null); + + const onSubmit = (e) => { + e.preventDefault(); + const data = Object.fromEntries(new FormData(e.currentTarget)); + + setSubmitted(data); + }; + + return ( + + ); +} diff --git a/apps/docs/content/components/input/custom-validation.ts b/apps/docs/content/components/input/custom-validation.ts new file mode 100644 index 0000000000..b0bf5b8588 --- /dev/null +++ b/apps/docs/content/components/input/custom-validation.ts @@ -0,0 +1,9 @@ +import App from "./custom-validation.raw.jsx?raw"; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/input/index.ts b/apps/docs/content/components/input/index.ts index 73664fc946..93c65cf335 100644 --- a/apps/docs/content/components/input/index.ts +++ b/apps/docs/content/components/input/index.ts @@ -14,6 +14,10 @@ import startEndContent from "./start-end-content"; import errorMessage from "./error-message"; import regexValidation from "./regex-validation"; import controlled from "./controlled"; +import builtInValidation from "./built-in-validation"; +import customValidation from "./custom-validation"; +import realTimeValidation from "./real-time-validation"; +import serverValidation from "./server-validation"; import customStyles from "./custom-styles"; import customImpl from "./custom-impl"; @@ -34,6 +38,10 @@ export const inputContent = { errorMessage, regexValidation, controlled, + builtInValidation, + customValidation, + realTimeValidation, + serverValidation, customStyles, customImpl, }; diff --git a/apps/docs/content/components/input/real-time-validation.raw.jsx b/apps/docs/content/components/input/real-time-validation.raw.jsx new file mode 100644 index 0000000000..cfb5d915c5 --- /dev/null +++ b/apps/docs/content/components/input/real-time-validation.raw.jsx @@ -0,0 +1,53 @@ +import {Button, Form, Input} from "@nextui-org/react"; + +export default function App() { + const [submitted, setSubmitted] = React.useState(null); + const [password, setPassword] = React.useState(""); + const errors = []; + + const onSubmit = (e) => { + e.preventDefault(); + const data = Object.fromEntries(new FormData(e.currentTarget)); + + setSubmitted(data); + }; + + if (password.length < 4) { + errors.push("Password must be 4 characters or more."); + } + if ((password.match(/[A-Z]/g) || []).length < 1) { + errors.push("Password must include at least 1 upper case letter"); + } + if ((password.match(/[^a-z0-9]/gi) || []).length < 1) { + errors.push("Password must include at least 1 symbol."); + } + + return ( + + ); +} diff --git a/apps/docs/content/components/input/real-time-validation.ts b/apps/docs/content/components/input/real-time-validation.ts new file mode 100644 index 0000000000..6f8034a877 --- /dev/null +++ b/apps/docs/content/components/input/real-time-validation.ts @@ -0,0 +1,9 @@ +import App from "./real-time-validation.raw.jsx?raw"; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/input/server-validation.raw.jsx b/apps/docs/content/components/input/server-validation.raw.jsx new file mode 100644 index 0000000000..3169541794 --- /dev/null +++ b/apps/docs/content/components/input/server-validation.raw.jsx @@ -0,0 +1,49 @@ +import {Button, Form, Input} from "@nextui-org/react"; + +export default function App() { + const [isLoading, setIsLoading] = React.useState(false); + const [errors, setErrors] = React.useState({}); + + const onSubmit = async (e) => { + e.preventDefault(); + setIsLoading(true); + + const data = Object.fromEntries(new FormData(e.currentTarget)); + const result = await callServer(data); + + setErrors(result.errors); + setIsLoading(false); + }; + + return ( + + ); +} + +// Fake server used in this example. +async function callServer(_) { + await new Promise((resolve) => setTimeout(resolve, 500)); + + return { + errors: { + username: "Sorry, this username is taken.", + }, + }; +} diff --git a/apps/docs/content/components/input/server-validation.ts b/apps/docs/content/components/input/server-validation.ts new file mode 100644 index 0000000000..84a2823b6a --- /dev/null +++ b/apps/docs/content/components/input/server-validation.ts @@ -0,0 +1,9 @@ +import App from "./server-validation.raw.jsx?raw"; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/docs/components/input.mdx b/apps/docs/content/docs/components/input.mdx index a2451d3010..bd2997ce14 100644 --- a/apps/docs/content/docs/components/input.mdx +++ b/apps/docs/content/docs/components/input.mdx @@ -125,6 +125,43 @@ You can use the `value` and `onValueChange` properties to control the input valu > **Note**: NextUI `Input` also supports native events like `onChange`, useful for form libraries > such as [Formik](https://formik.org/) and [React Hook Form](https://react-hook-form.com/). +### With Form + +`Input` can be used with a `Form` component to leverage form state management. By default, `Form` components use `validationBehavior="aria"`, which will not block form submission if any inputs are invalid. For more on form and validation behaviors, see the [Forms](/docs/guide/forms) guide. + +#### Built-in Validation + +`Input` supports the following [native HTML constraints](https://developer.mozilla.org/docs/Web/HTML/Constraint_validation): + +- `isRequired` indicates that a field must have a value before the form can be submitted. +- `minLength` and `maxLength` specify the minimum and length of text input. +- `pattern` provides a custom regular expression that a text input must conform to. +- `type="email"` and `type="url"` provide built-in validation for email addresses and URLs. + +When using native validation, error messages can be customized by passing a function to `errorMessage` and checking the [ValidityState](https://developer.mozilla.org/docs/Web/API/ValidityState) of `validationDetails`. + +