Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(form): use native as default validation behavior #4425

Open
wants to merge 7 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/flat-ducks-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nextui-org/form": minor
---

changed form default validation behavior to native
2 changes: 1 addition & 1 deletion apps/docs/content/components/form/controlled.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage={({validationDetails}) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Form, Input, Button} from "@nextui-org/react";

export default function App() {
const onSubmit = (e) => {
e.preventDefault();
};

return (
<Form className="w-full max-w-xs" validationBehavior="aria" onSubmit={onSubmit}>
<Input
isRequired
label="Username"
labelPlacement="outside"
name="username"
placeholder="Enter your username"
type="text"
validate={(value) => {
if (value.length < 3) {
return "Username must be at least 3 characters long";
}

return value === "admin" ? "Nice try!" : null;
}}
/>
<Button type="submit" variant="bordered">
Submit
</Button>
</Form>
);
}
9 changes: 9 additions & 0 deletions apps/docs/content/components/form/custom-validation-aria.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import App from "./custom-validation-aria.raw.jsx?raw";

const react = {
"/App.jsx": App,
};

export default {
...react,
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
label="Username"
Expand Down
3 changes: 1 addition & 2 deletions apps/docs/content/components/form/demo.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ export default function App() {

return (
<Form
className="w-full justify-center items-center w-full space-y-4"
validationBehavior="native"
className="w-full justify-center items-center space-y-4"
validationErrors={errors}
onReset={() => setSubmitted(null)}
onSubmit={onSubmit}
Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/components/form/events.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export default function App() {
return (
<Form
className="w-full max-w-xs flex flex-col gap-4"
validationBehavior="native"
onReset={() => setAction("reset")}
onSubmit={(e) => {
e.preventDefault();
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/components/form/form-usage.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/content/components/form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import controlled from "./controlled";
import nativeValidation from "./native-validation";
import customErrorMessages from "./custom-error-messages";
import customValidation from "./custom-validation";
import customValidationAria from "./custom-validation-aria";
import realTimeValidation from "./real-time-validation";
import serverValidation from "./server-validation";
import events from "./events";
Expand All @@ -15,6 +16,7 @@ export const formContent = {
nativeValidation,
customErrorMessages,
customValidation,
customValidationAria,
realTimeValidation,
serverValidation,
events,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/components/form/usage.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export default function App() {
return (
<Form
className="flex w-full flex-col items-start gap-4"
validationBehavior="native"
onSubmit={(e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
Expand All @@ -21,7 +20,6 @@ export default function App() {
length={4}
name="otp"
placeholder="Enter code"
validationBehavior="native"
/>
<Button size="sm" type="submit" variant="bordered">
Submit
Expand Down
1 change: 0 additions & 1 deletion apps/docs/content/components/input-otp/required.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export default function App() {
length={4}
name="otp"
placeholder="Enter code"
validationBehavior="native"
/>
<Button size="sm" type="submit" variant="bordered">
Submit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage={({validationDetails, validationErrors}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function App() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
label="Username"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function App() {
}

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
errorMessage={() => (
<ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ export default function App() {
};

return (
<Form
className="w-full max-w-xs"
validationBehavior="native"
validationErrors={errors}
onSubmit={onSubmit}
>
<Form className="w-full max-w-xs" validationErrors={errors} onSubmit={onSubmit}>
<Input
isRequired
isDisabled={isLoading}
Expand Down
20 changes: 10 additions & 10 deletions apps/docs/content/docs/api-references/nextui-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: API References for NextUI Provider

# NextUI Provider

API reference for the `NextUIProvider`.
API reference for the `NextUIProvider`.

------

Expand Down Expand Up @@ -67,11 +67,11 @@ Here's the supported locales. By default, It is `en-US`.
```tsx
const localeValues = [
'fr-FR', 'fr-CA', 'de-DE', 'en-US', 'en-GB', 'ja-JP',
'da-DK', 'nl-NL', 'fi-FI', 'it-IT', 'nb-NO', 'es-ES',
'sv-SE', 'pt-BR', 'zh-CN', 'zh-TW', 'ko-KR', 'bg-BG',
'hr-HR', 'cs-CZ', 'et-EE', 'hu-HU', 'lv-LV', 'lt-LT',
'pl-PL', 'ro-RO', 'ru-RU', 'sr-SP', 'sk-SK', 'sl-SI',
'tr-TR', 'uk-UA', 'ar-AE', 'ar-DZ', 'AR-EG', 'ar-SA',
'da-DK', 'nl-NL', 'fi-FI', 'it-IT', 'nb-NO', 'es-ES',
'sv-SE', 'pt-BR', 'zh-CN', 'zh-TW', 'ko-KR', 'bg-BG',
'hr-HR', 'cs-CZ', 'et-EE', 'hu-HU', 'lv-LV', 'lt-LT',
'pl-PL', 'ro-RO', 'ru-RU', 'sr-SP', 'sk-SK', 'sl-SI',
'tr-TR', 'uk-UA', 'ar-AE', 'ar-DZ', 'AR-EG', 'ar-SA',
'el-GR', 'he-IL', 'fa-AF', 'am-ET', 'hi-IN', 'th-TH'
];
```
Expand Down Expand Up @@ -112,7 +112,7 @@ interface AppProviderProps {

`createCalendar`

- **Description**:
- **Description**:
This function helps to reduce the bundle size by providing a custom calendar system.

By default, this includes all calendar systems supported by `@internationalized/date`. However,
Expand Down Expand Up @@ -173,14 +173,14 @@ interface AppProviderProps {

`validationBehavior`

- **Description**: Whether to use native HTML form validation to prevent form submission when the value is missing or invalid,
- **Description**: Whether to use native HTML form validation to prevent form submission when the value is missing or invalid,
or mark the field as required or invalid via ARIA.
- **Type**: `native | aria`
- **Default**: `aria`
- **Default**: `native`

`reducedMotion`

- **Description**: Controls the motion preferences for the entire application, allowing developers to respect user settings for reduced motion.
- **Description**: Controls the motion preferences for the entire application, allowing developers to respect user settings for reduced motion.
The available options are:
- `"user"`: Adapts to the user's device settings for reduced motion.
- `"always"`: Disables all animations.
Expand Down
9 changes: 5 additions & 4 deletions apps/docs/content/docs/components/form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ See the [Forms](/docs/guide/forms) guide to learn more about form validation, in

### Validation Behavior

`Form` validation uses ARIA attributes by default, but can be switched to native HTML validation by setting `validationBehavior="native"`. ARIA validation shows realtime errors without blocking submission. This can be set at the form or field level.
`Form` validation uses native validation behavior by default, but can be switched to ARIA validation by setting `validationBehavior="aria"`. ARIA validation shows realtime errors without blocking submission. This can be set at the form or field level.
To set the default behavior at the app level, you can change the form defaults for your entire app using [NextUI Provider](/docs/api-references/nextui-provider).


```tsx
<Form validationBehavior="native">
<Form validationBehavior="aria">
<Input
isRequired
name="username"
Expand All @@ -95,7 +96,7 @@ See the [Forms](/docs/guide/forms) guide to learn more about form validation, in
</Form>
```

<CodeDemo title="Validation Behavior" files={formContent.customValidation} />
<CodeDemo title="Validation Behavior" files={formContent.customValidationAria} />

## Accessibility

Expand Down Expand Up @@ -123,7 +124,7 @@ See the [Forms](/docs/guide/forms) guide to learn more about form validation, in
type: "'native' | 'aria'",
description:
"Whether to use native HTML form validation to prevent form submission when a field value is missing or invalid, or mark fields as required or invalid via ARIA.",
default: "aria",
default: "native",
},
{
attribute: "validationErrors",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/components/input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ You can use the `value` and `onValueChange` properties to control the input valu

### 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.
`Input` can be used with a `Form` component to leverage form state management. For more on form and validation behaviors, see the [Forms](/docs/guide/forms) guide.

#### Built-in Validation

Expand Down
14 changes: 7 additions & 7 deletions apps/docs/content/docs/guide/forms.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function Example() {
};

return (
<Form onSubmit={onSubmit} validationBehavior="native">
<Form onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down Expand Up @@ -111,7 +111,7 @@ function Example() {
};

return (
<Form className="w-full max-w-xs" validationBehavior="native" onSubmit={onSubmit}>
<Form className="w-full max-w-xs" onSubmit={onSubmit}>
<Input
isRequired
errorMessage="Please enter a valid email"
Expand Down Expand Up @@ -155,7 +155,7 @@ export default function App() {
};

return (
<Form validationBehavior="native" onSubmit={onSubmit}>
<Form onSubmit={onSubmit}>
<Input
isRequired
errorMessage={({validationDetails}) => {
Expand Down Expand Up @@ -208,7 +208,7 @@ export default function App() {
};

return (
<Form validationBehavior="native" onSubmit={onSubmit}>
<Form onSubmit={onSubmit}>
<Input
isRequired
label="Email"
Expand All @@ -225,8 +225,8 @@ export default function App() {
}
```

To enable native validation, set `validationBehavior="native"`.
By default, `validationBehavior="aria"` is set, which only marks the field as required or invalid for assistive technologies, without preventing form submission.
To enable ARIA validation, set `validationBehavior="aria"`.
When`validationBehavior="aria"` is set, fields are only marked as required or invalid for assistive technologies, without preventing form submission.
You can change the form defaults for your entire app using [NextUI Provider](/docs/api-references/nextui-provider).

Supported constraints include:
Expand Down Expand Up @@ -256,7 +256,7 @@ export default function App() {
};

return (
<Form validationBehavior="native" onSubmit={onSubmit}>
<Form onSubmit={onSubmit}>
<Input
isRequired
label="Username"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -653,8 +653,8 @@ describe("Autocomplete", () => {
describe("validationBehavior=native", () => {
it("supports isRequired", async () => {
const {getByTestId, getByRole, findByRole} = render(
<Form data-testid="form">
<AutocompleteExample isRequired validationBehavior="native" />
<Form data-testid="form" validationBehavior="native">
<AutocompleteExample isRequired />
</Form>,
);

Expand Down Expand Up @@ -695,8 +695,8 @@ describe("Autocomplete", () => {
};

return (
<Form validationErrors={serverErrors} onSubmit={onSubmit}>
<AutocompleteExample data-testid="input" name="value" validationBehavior="native" />
<Form validationBehavior="native" validationErrors={serverErrors} onSubmit={onSubmit}>
<AutocompleteExample data-testid="input" name="value" />
<button data-testid="submit" type="submit">
Submit
</button>
Expand Down Expand Up @@ -778,7 +778,7 @@ describe("Autocomplete", () => {

it("supports server validation", async () => {
const {getByTestId, getByRole} = render(
<Form validationErrors={{value: "Invalid value"}}>
<Form validationBehavior="aria" validationErrors={{value: "Invalid value"}}>
<AutocompleteExample data-testid="input" name="value" />
</Form>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,8 @@ describe("Checkbox.Group", () => {
};

return (
<Form validationErrors={serverErrors} onSubmit={onSubmit}>
<CheckboxGroup
label="Agree to the following"
name="terms"
validationBehavior="native"
>
<Form validationBehavior="native" validationErrors={serverErrors} onSubmit={onSubmit}>
<CheckboxGroup label="Agree to the following" name="terms">
<Checkbox value="terms">Terms and conditions</Checkbox>
<Checkbox value="cookies">Cookies</Checkbox>
<Checkbox value="privacy">Privacy policy</Checkbox>
Expand Down
Loading
Loading