From 024f8e0810b23071f419c7804cc52ecc458d8412 Mon Sep 17 00:00:00 2001 From: Alexandre Asselin Date: Tue, 3 Dec 2024 15:47:18 -0500 Subject: [PATCH] add 3 guides with examples --- .../concepts/client-side-routing.mdx | 76 ++++++- .../components/concepts/controlled-mode.mdx | 43 +++- .../content/components/concepts/forms.mdx | 62 +++++- apps/docs/examples/Preview.ts | 18 ++ apps/docs/package.json | 1 + package.json | 2 + .../Form/docs/forms-concept/accessValue.tsx | 24 +++ .../docs/forms-concept/customValidation.tsx | 32 +++ .../Form/docs/forms-concept/errorMessage.tsx | 20 ++ .../src/Form/docs/forms-concept/formData.tsx | 16 ++ .../src/Form/docs/forms-concept/labels.tsx | 10 + .../docs/forms-concept/nativeValidation.tsx | 13 ++ .../Form/docs/forms-concept/reactHookForm.tsx | 46 +++++ .../docs/controlled-mode/controlled.tsx | 27 +++ .../docs/controlled-mode/uncontrolled.tsx | 15 ++ pnpm-lock.yaml | 193 ++---------------- 16 files changed, 415 insertions(+), 183 deletions(-) create mode 100644 packages/components/src/Form/docs/forms-concept/accessValue.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/customValidation.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/errorMessage.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/formData.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/labels.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/nativeValidation.tsx create mode 100644 packages/components/src/Form/docs/forms-concept/reactHookForm.tsx create mode 100644 packages/components/src/HopperProvider/docs/controlled-mode/controlled.tsx create mode 100644 packages/components/src/HopperProvider/docs/controlled-mode/uncontrolled.tsx diff --git a/apps/docs/content/components/concepts/client-side-routing.mdx b/apps/docs/content/components/concepts/client-side-routing.mdx index ab9480db2..60b6785f7 100644 --- a/apps/docs/content/components/concepts/client-side-routing.mdx +++ b/apps/docs/content/components/concepts/client-side-routing.mdx @@ -2,15 +2,87 @@ title: Client Side Routing description: Many Hopper components support rendering as HTML links. This page discusses how to set up your app to integrate Hopper links with your framework or client side router. order: 4 -status: WIP --- -This page will be heavily inspired by https://react-spectrum.adobe.com/react-spectrum/routing.html +_Since Hopper components are designed on top of React Aria, this article will be heavily inspired by the [Client Side Routing](https://react-spectrum.adobe.com/react-spectrum/routing.html) article in React-Aria's documentation._ ## Introduction +Hopper components such as [Link](./navigation/Link), Menu, Tabs, Table, and many others support rendering elements as links that perform navigation when the user interacts with them. Each component that supports link behavior accepts the href prop, which causes the component to render an `` element. Other link DOM props such as target and download are also supported. + +Depending on the component, users may interact with links in different ways. For example, users can navigate between tabs using the arrow keys, or open a link in a ComboBox using the enter key. Because Hopper components accept the href prop rather than supporting arbitrary element overrides, they can ensure that link navigation occurs when it is appropriate for the component. + +By default, links perform native browser navigation when they are interacted with. However, many apps and frameworks use client side routers to avoid a full page reload when navigating between pages. The `HopperProvider` component configures all Hopper components within it to navigate using the client side router you provide. Set this up once in the root of your app, and any Hopper component with the href prop will automatically navigate using your router. + ## Provider setup +The HopperProvider component accepts two props: `navigate` and `useHref`. `navigate` should be set to a function received from your router for performing a client side navigation programmatically. `useHref` is an optional prop that converts a router-specific href to a native HTML href, e.g. prepending a base path. The following example shows the general pattern. Framework-specific examples are shown below. + +```tsx +import { HopperProvider } from "@hopper-ui/components"; +import { useNavigate, useHref } from "your-router"; + +function App() { + let navigate = useNavigate(); + + return ( + + {/* ... */} + + ); +} +``` + ### Router options +All link components accept a `routerOptions` prop, which is an object that is passed through to the client side router's navigate function as the second argument. This can be used to control any router-specific behaviors, such as scrolling, replacing instead of pushing to the history, etc. + +```tsx + + {/* ...*/} + +``` +When using TypeScript, you can configure the RouterConfig type globally so that all link components have auto complete and type safety using a type provided by your router. + +```tsx +import type { RouterOptions } from "your-router"; + +declare module "react-aria-components" { + interface RouterConfig { + routerOptions: RouterOptions + } +} +``` + ### React Router + +The [useNavigate](https://reactrouter.com/en/main/hooks/use-navigate) hook from `react-router-dom` returns a navigate function you can pass to `HopperProvider`. The [useHref](https://reactrouter.com/en/main/hooks/use-href) hook can also be provided if you're using React Router's `basename` option. Ensure that the component that calls `useNavigate` and renders `HopperProvider` is inside the router component (e.g. BrowserRouter) so that it has access to React Router's internal context. The React Router `` element should also be defined inside Hopper's `` so that links inside the rendered routes have access to the router. + +```tsx +import { BrowserRouter, type NavigateOptions, useHref, useNavigate } from "react-router-dom"; +import { HopperProvider } from "@hopper-ui/components"; + +declare module "react-aria-components" { + interface RouterConfig { + routerOptions: NavigateOptions; + } +} + +function App() { + let navigate = useNavigate(); + + return ( + + {/* Your app here... */} + + } /> + {/* ... */} + + + ); +} + + + + +``` diff --git a/apps/docs/content/components/concepts/controlled-mode.mdx b/apps/docs/content/components/concepts/controlled-mode.mdx index 5277e209f..7590a109b 100644 --- a/apps/docs/content/components/concepts/controlled-mode.mdx +++ b/apps/docs/content/components/concepts/controlled-mode.mdx @@ -1,6 +1,45 @@ --- title: Controlled Mode -description: Controlled vs Uncontrolled mode in Hopper components order: 4 -status: WIP --- +When working with Hopper components, you can customize a component's behavior using controlled or uncontrolled properties, depending on your needs. This flexibility is particularly useful for implementing interactive features or modifying the default behavior of components while maintaining their visual style and structure. + +**Tip**: To dive deeper into the concept of controlled and uncontrolled components in React, read [React's guide here](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). + +## Uncontrolled Mode + +**Uncontrolled mode** is suitable for scenarios where you don’t need to respond programmatically to user interactions. + +In uncontrolled mode, the component manages its internal state. You provide an initial value using _defaultX_ properties, and the component updates its state automatically in response to user interactions. + +For example, to create a TagGroup where some tags are initially selected, use the defaultSelectedKeys prop: + + + +In this example: +- `defaultSelectedKeys`: Specifies the initially selected items. +- The component handles the selection state internally. + +## Controlled Mode + +**Controlled mode** is suitable for scenarios where the component's state depends on external data or when you need to respond programmatically to user interactions + +In controlled mode, you manage the state of the component externally by providing the _X_ and _onXChanged_ properties. This allows for full control over the component's behavior and is ideal for complex interactions or when the component's state is derived from external logic. + +For example, to fully manage the selected tags: + + + +In this example: +- `selectedKeys`: Represents the current selection, controlled externally. +- `onSelectionChange`: Callback invoked when the selection changes, allowing you to update the external state. + +## Choosing Between Controlled and Uncontrolled Modes +- Use uncontrolled mode (defaultX) for simpler use cases where internal state management by the component suffices. +- Use controlled mode (X and onXChanged) when external logic or advanced control is required. + +By leveraging these modes, you can tailor Hopper components to meet your application's functional requirements while maintaining consistency and reusability. + + + + diff --git a/apps/docs/content/components/concepts/forms.mdx b/apps/docs/content/components/concepts/forms.mdx index f54bb4a63..25b47fe54 100644 --- a/apps/docs/content/components/concepts/forms.mdx +++ b/apps/docs/content/components/concepts/forms.mdx @@ -2,19 +2,75 @@ title: Forms description: Forms allow users to enter and submit data, and provide them with feedback along the way. Hopper includes many components that integrate with HTML forms, with support for custom validation, labels, and help text. order: 6 -status: WIP --- -This page will be heavily inspired by https://react-spectrum.adobe.com/react-spectrum/forms.html +_Since Hopper components are designed on top of React Aria, this article will be heavily inspired by the [Forms](https://react-spectrum.adobe.com/react-spectrum/forms.html) article in React-Aria's documentation._ ## Labels and help text +Accessible forms start with clear, descriptive labels for each field. All Hopper form components support labeling using the `label` prop. In addition, help text associates additional context with a field such as a description or error message. + + + +Most fields should have a visible label. In rare exceptions, the aria-label or aria-labelledby attribute must be provided instead to identify the element. + ## Submitting data +How you submit form data depends on your framework, application, and server. By default, HTML forms are submitted by the browser using a full page refresh. You can take control of form submission by calling preventDefault during the onSubmit event, and make an API call to submit the data however you like. + +### Uncontrolled forms + +The simplest way to get data from a form is using the browser's FormData API during the onSubmit event. This can be passed directly to [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch), or converted into a regular JavaScript object using [Object.fromEntries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries). Each field should have a name prop to identify it, and values will be serialized to strings by the browser. + + + +By default, all Hopper components are uncontrolled, which means that the state is stored internally on your behalf. If you need access to the value in realtime, as the user is editing, you can make it controlled. You'll need to manage the state using React's [useState](https://react.dev/reference/react/useState) hook, and pass the current value and a change handler into each form component. + + + ## Validation +Form validation is important to ensure user input is in an expected format and meets business requirements. Well-designed form validation assists the user with specific, helpful error messages without confusing and frustrating them with unnecessary errors on partial input. Hopper supports native HTML constraint validation with customizable UI, custom validation functions, realtime validation, and integration with server-side validation errors. + +### Built-in validation + +All Hopper form components integrate with native HTML [constraint validation](https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation). This allows you to define constraints on each field such as required, minimum and maximum values, text formats such as email addresses, and even custom regular expression patterns. These constraints are checked by the browser when the user commits changes to the value (e.g. on blur) or submits the form. + +To enable native validation, set the `validationBehavior="native"` prop on the [Form](../forms/Form) component. This example shows a required email field, which is validated by the browser and displayed with a help text. + + + +Supported constraints include: + +- `isRequired` indicates that a field must have a value before the form can be submitted. +- `minValue` and `maxValue` specify the minimum and maximum value in a date picker or number field. +- `minLength` and `maxLength` specify the minimum and length of text input. +- `pattern` provides a custom [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions) that a text input must conform to. +- `type="email"` and `type="url"` provide builtin validation for email addresses and URLs. + +### Customizing error messages + +By default, Hopper displays the error message provided by the browser, which is localized in the user's preferred language. You can customize these messages by providing a function to the `errorMessage` prop. This receives a list of error strings along with a [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) object describing why the field is invalid, and should return an error message to display. + + + +**Note**: The default error messages are localized by the browser using the browser/operating system language setting. Hopper's Provider has no effect on validation errors. + +### Custom validation + +In addition to the built-in constraints, custom validation is supported by providing a function to the validate prop. This function receives the current field value, and can return a string or array of strings representing one or more error messages. These are displayed to the user after the value is committed (e.g. on blur) to avoid distracting them on each keystroke. + + + ## Form libraries +In most cases, uncontrolled forms with the builtin validation features are enough. However, if you are building a truly complex form, or integrating Hopper components into an existing form, a separate form library such as [React Hook Form](https://react-hook-form.com/) or [Formik](https://formik.org/) may be helpful. + + ### React Hook Form -{/* TODO: react-hook-form + valibot? */} +[React Hook Form](https://react-hook-form.com/) is a popular form library for React. It is primarily designed to work directly with plain HTML input elements, but supports custom form components like the ones in Hopper as well. + +Since Hopper manages the state for components internally, you can use the (Controller)[https://react-hook-form.com/docs/usecontroller/controller] component from React Hook Form to integrate Hopper components. Pass the props for the `field` render prop through to the Hopper component you're using, and use the `fieldState` to get validation errors to display. + + diff --git a/apps/docs/examples/Preview.ts b/apps/docs/examples/Preview.ts index 75e6cf828..8b53f2d40 100644 --- a/apps/docs/examples/Preview.ts +++ b/apps/docs/examples/Preview.ts @@ -227,6 +227,24 @@ export const Previews: Record = { "HopperProvider/docs/color-scheme/useColorSchemeValue": { component: lazy(() => import("@/../../packages/components/src/HopperProvider/docs/color-scheme/useColorSchemeValue.tsx")) }, + "Form/docs/forms-concept/labels": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/labels.tsx")) + }, + "Form/docs/forms-concept/formData": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/formData.tsx")) + }, + "Form/docs/forms-concept/accessValue": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/accessValue.tsx")) + }, + "Form/docs/forms-concept/nativeValidation": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/nativeValidation.tsx")) + }, + "Form/docs/forms-concept/errorMessage": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/errorMessage.tsx")) + }, + "Form/docs/forms-concept/customValidation": { + component: lazy(() => import("@/../../packages/components/src/Form/docs/forms-concept/customValidation.tsx")) + }, "layout/docs/flex": { component: lazy(() => import("@/../../packages/components/src/layout/docs/flex.tsx")) }, diff --git a/apps/docs/package.json b/apps/docs/package.json index 3e65387fe..7145fe83d 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -30,6 +30,7 @@ "react-aria": "^3.36", "react-aria-components": "^1.5", "react-dom": "18.3.1", + "react-hook-form": "7.53.2", "react-toggle": "4.1.3", "rehype-parse": "^9.0.1", "rehype-pretty-code": "0.14.0", diff --git a/package.json b/package.json index 5c64914c3..c4df8ccb5 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "build:apps": "pnpm -r --filter \"{apps/**}\" build ", "changeset": "changeset", "ci-release": "pnpm build:pkg && changeset publish", + "aa": "cd ./apps/docs && pnpm generate:previewRef", "generate-icons": "pnpm --filter=\"svg-icons\" generate-icons && pnpm --filter=\"@hopper-ui/icons*\" generate-icons", "lint": "pnpm run \"/^lint:.*/\"", "lint:eslint": "eslint . --max-warnings=0 --cache --cache-location node_modules/.cache/eslint", @@ -80,6 +81,7 @@ "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", + "react-hook-form": "7.53.2", "react-refresh": "0.14.2", "react-router-dom": "6.27.0", "storybook": "^8.4.5", diff --git a/packages/components/src/Form/docs/forms-concept/accessValue.tsx b/packages/components/src/Form/docs/forms-concept/accessValue.tsx new file mode 100644 index 000000000..0a6d0c666 --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/accessValue.tsx @@ -0,0 +1,24 @@ +import { Button, ButtonGroup, Form, TextField } from "@hopper-ui/components"; +import { useState, type FormEvent } from "react"; + +export default function Example() { + let [name, setName] = useState(""); + + let onSubmit = (e: FormEvent) => { + e.preventDefault(); + + // Submit data to your backend API... + alert(name); + }; + + return ( +
+ +
You entered: {name}
+ + + + + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/customValidation.tsx b/packages/components/src/Form/docs/forms-concept/customValidation.tsx new file mode 100644 index 000000000..ef4ee77ad --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/customValidation.tsx @@ -0,0 +1,32 @@ +import { Button, ButtonGroup, Form, TextField } from "@hopper-ui/components"; +import { useState, type FormEvent } from "react"; + +export default function Example() { + let [submitted, setSubmitted] = useState | null>(null); + + let onSubmit = (e: FormEvent) => { + // Prevent default browser page refresh. + e.preventDefault(); + + // Get form data as an object. + let data = Object.fromEntries(new FormData(e.currentTarget)); + + // Submit to your backend API... + setSubmitted(data); + }; + + return ( +
+ + + + + + {submitted && ( +
+ You submitted: {JSON.stringify(submitted)} +
+ )} + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/errorMessage.tsx b/packages/components/src/Form/docs/forms-concept/errorMessage.tsx new file mode 100644 index 000000000..7d8ddcd79 --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/errorMessage.tsx @@ -0,0 +1,20 @@ +import { Button, ButtonGroup, Form, TextField } from "@hopper-ui/components"; + +export default function Example() { + return ( +
+ ( + validationDetails.valueMissing ? "Please enter a name." : "" + )} + /> + + + + + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/formData.tsx b/packages/components/src/Form/docs/forms-concept/formData.tsx new file mode 100644 index 000000000..41cb02f83 --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/formData.tsx @@ -0,0 +1,16 @@ +import { Button, ButtonGroup, Form, TextField } from "@hopper-ui/components"; + +export default function Example() { + return ( +
+ value === 'admin' ? 'Nice try!' : null} + /> + + + + + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/labels.tsx b/packages/components/src/Form/docs/forms-concept/labels.tsx new file mode 100644 index 000000000..e9d3fa067 --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/labels.tsx @@ -0,0 +1,10 @@ +import { PasswordField } from "@hopper-ui/components"; + +export default function Example() { + return ( + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/nativeValidation.tsx b/packages/components/src/Form/docs/forms-concept/nativeValidation.tsx new file mode 100644 index 000000000..6635fb51e --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/nativeValidation.tsx @@ -0,0 +1,13 @@ +import { Button, ButtonGroup, Form, TextField } from "@hopper-ui/components"; + +export default function Example() { + return ( +
+ + + + + + + ); +} diff --git a/packages/components/src/Form/docs/forms-concept/reactHookForm.tsx b/packages/components/src/Form/docs/forms-concept/reactHookForm.tsx new file mode 100644 index 000000000..66f121cfb --- /dev/null +++ b/packages/components/src/Form/docs/forms-concept/reactHookForm.tsx @@ -0,0 +1,46 @@ +import { Button, Form, TextField } from "@hopper-ui/components"; +import { Controller, useForm } from "react-hook-form"; + +type FormValues = { + name: string +} + + +export default function Example() { + const {handleSubmit, control} = useForm({ + defaultValues: { + name: "", + }, + }); + + const onSubmit = (data: FormValues) => { + // Call your API here... + }; + + return ( +
+ ( + + )} + /> + + + ); +} diff --git a/packages/components/src/HopperProvider/docs/controlled-mode/controlled.tsx b/packages/components/src/HopperProvider/docs/controlled-mode/controlled.tsx new file mode 100644 index 000000000..f8090c2fb --- /dev/null +++ b/packages/components/src/HopperProvider/docs/controlled-mode/controlled.tsx @@ -0,0 +1,27 @@ +import { Tag, TagGroup, type Selection } from "@hopper-ui/components"; +import { useState } from "react"; + +export default function Example() { + const [selectedKeys, setSelectedKeys] = useState(["designer"]); + + const handleSelectionChange = (newSelectedKeys: Selection) => { + if(newSelectedKeys === "all") { + setSelectedKeys(["designer", "developer", "manager"]); + } else { + setSelectedKeys([...Array.from(newSelectedKeys).map(x => x.toString())]); + } + }; + + return ( + + Designer + Developer + Manager + + ); +} diff --git a/packages/components/src/HopperProvider/docs/controlled-mode/uncontrolled.tsx b/packages/components/src/HopperProvider/docs/controlled-mode/uncontrolled.tsx new file mode 100644 index 000000000..b790a0528 --- /dev/null +++ b/packages/components/src/HopperProvider/docs/controlled-mode/uncontrolled.tsx @@ -0,0 +1,15 @@ +import { Tag, TagGroup } from "@hopper-ui/components"; + +export default function Example() { + return ( + + Designer + Developer + Manager + + ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51f4dd1f8..284c54866 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,6 +116,9 @@ importers: react-dom: specifier: 18.3.1 version: 18.3.1(react@18.3.1) + react-hook-form: + specifier: 7.53.2 + version: 7.53.2(react@18.3.1) react-refresh: specifier: 0.14.2 version: 0.14.2 @@ -179,6 +182,9 @@ importers: react-dom: specifier: 18.3.1 version: 18.3.1(react@18.3.1) + react-hook-form: + specifier: 7.53.2 + version: 7.53.2(react@18.3.1) react-toggle: specifier: 4.1.3 version: 4.1.3(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -324,7 +330,7 @@ importers: version: 2.0.1 '@workleap/eslint-plugin': specifier: 3.2.2 - version: 3.2.2(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4) + version: 3.2.2(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4) '@workleap/swc-configs': specifier: 2.2.3 version: 2.2.3(@swc/core@1.7.26(@swc/helpers@0.5.13))(@swc/helpers@0.5.13)(@swc/jest@0.2.36(@swc/core@1.7.26(@swc/helpers@0.5.13)))(browserslist@4.23.3) @@ -9190,6 +9196,12 @@ packages: peerDependencies: react: ^18.3.1 + react-hook-form@7.53.2: + resolution: {integrity: sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -12752,42 +12764,6 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.7.5 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - optional: true - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4))': dependencies: '@jest/console': 29.7.0 @@ -15906,32 +15882,6 @@ snapshots: '@workleap/browserslist-config@2.0.1': {} - '@workleap/eslint-plugin@3.2.2(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4)': - dependencies: - '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) - eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4) - eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) - eslint-plugin-mdx: 3.1.5(eslint@8.57.1) - eslint-plugin-package-json: 0.10.4(eslint@8.57.1)(jsonc-eslint-parser@2.4.0) - eslint-plugin-react: 7.37.2(eslint@8.57.1) - eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) - eslint-plugin-storybook: 0.8.0(eslint@8.57.1)(typescript@5.5.4) - eslint-plugin-testing-library: 6.5.0(eslint@8.57.1)(typescript@5.5.4) - eslint-plugin-yml: 1.15.0(eslint@8.57.1) - jsonc-eslint-parser: 2.4.0 - yaml-eslint-parser: 1.2.3 - optionalDependencies: - '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.5.4) - eslint: 8.57.1 - typescript: 5.5.4 - transitivePeerDependencies: - - bluebird - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - jest - - supports-color - '@workleap/eslint-plugin@3.2.2(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4)': dependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) @@ -16965,22 +16915,6 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 - create-jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - optional: true - create-jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): dependencies: '@jest/types': 29.6.3 @@ -17791,17 +17725,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4): - dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.5.4) - eslint: 8.57.1 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) - jest: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)))(typescript@5.5.4): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.5.4) @@ -19498,26 +19421,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - optional: true - jest-cli@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) @@ -19537,38 +19440,6 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.7.5 - ts-node: 10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - optional: true - jest-config@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): dependencies: '@babel/core': 7.26.0 @@ -19899,19 +19770,6 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - optional: true - jest@29.7.0(@types/node@22.7.5)(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4)) @@ -22285,6 +22143,10 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-hook-form@7.53.2(react@18.3.1): + dependencies: + react: 18.3.1 + react-is@16.13.1: {} react-is@17.0.2: {} @@ -23621,27 +23483,6 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.0) esbuild: 0.23.1 - ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.7.5 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.5.4 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.7.26(@swc/helpers@0.5.13) - optional: true - ts-node@10.9.2(@swc/core@1.7.36(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1