Merge pull request #3 from cupcakearmy/1

update deps
This commit is contained in:
Nicco 2023-03-03 23:47:16 +01:00 committed by GitHub
commit b3865cc243
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 589 additions and 364 deletions

19
.github/actions/build/action.yaml vendored Normal file
View File

@ -0,0 +1,19 @@
name: "Build"
description: "Build the project."
runs:
using: "composite" # This is the magic
steps:
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
registry-url: https://registry.npmjs.org/
- run: pnpm install --frozen-lockfile
shell: bash
- run: pnpm run build
shell: bash

View File

@ -23,16 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- run: pnpm install
- run: pnpm run build
- uses: ./.github/actions/build
- uses: actions/configure-pages@v3
- uses: actions/upload-pages-artifact@v1

18
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Release
on:
push:
tags:
- "v*.*.*"
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/build
- run: pnpm test
- run: pnpm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -14,14 +14,5 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm run compile
- uses: ./.github/actions/build
- run: pnpm run test

View File

@ -1,9 +1,9 @@
![Logo](https://raw.githubusercontent.com/cupcakearmy/formhero/master/.github/Logo.jpg)
![dependencies](https://badgen.net/david/dep/cupcakearmy/formhero)
![downloads badge](https://badgen.net/npm/dt/formhero)
![types badge](https://badgen.net/npm/types/formhero)
![version badge](https://badgen.net/npm/v/formhero)
![types badge](https://badgen.net/npm/types/formhero)
![downloads badge](https://badgen.net/npm/dt/formhero)
![dependencies](https://badgen.net//bundlephobia/dependency-count/formhero)
![minzip size badge](https://badgen.net/bundlephobia/minzip/formhero)
**Fully customisable react form utility.**
@ -22,7 +22,7 @@
npm i formhero
```
*Note:* Requires at least typescript version `3.5`, otherwise the error object will not have the right inherited types.
_Note:_ Requires at least typescript version `3.5`, otherwise the error object will not have the right inherited types.
### 👁 Demos
@ -99,7 +99,7 @@ const Form = () => {
password: '',
},
{
username: value => value.length > 3,
username: (value) => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
@ -146,7 +146,7 @@ const Form = () => {
return (
<form
onSubmit={e => {
onSubmit={(e) => {
e.preventDefault()
console.log(form)
}}
@ -159,7 +159,7 @@ const Form = () => {
{...field('awesome', {
setter: 'checked',
getter: 'onChange',
extractor: e => e.target.checked,
extractor: (e) => e.target.checked,
})}
/>
Is it awesome?
@ -176,28 +176,28 @@ const Form = () => {
Sometimes you don't know all the fields upfront. You can simply define a generic type and assign it to the initial object. Of course type assistance is limited in this case as formhero cannot be sure what keys are valid.
```typescript
import React from "react";
import ReactDOM from "react-dom";
import { useForm } from "formhero";
import React from 'react'
import ReactDOM from 'react-dom'
import { useForm } from 'formhero'
type MyForm = { [field: string]: string | number };
type MyForm = { [field: string]: string | number }
const init: MyForm = {
username: "unicorn",
password: ""
};
username: 'unicorn',
password: '',
}
const Form: React.FC = () => {
const { field, form, errors } = useForm(init);
const { field, form, errors } = useForm(init)
return (
<form>
<input {...field("username")} placeholder="Username" />
<input {...field("someother")} placeholder="Something else" />
<input {...field("password")} placeholder="Password" type="password" />
<input {...field('username')} placeholder="Username" />
<input {...field('someother')} placeholder="Something else" />
<input {...field('password')} placeholder="Password" type="password" />
</form>
);
};
)
}
```
## 📖 Documentation
@ -267,9 +267,9 @@ const validators = {
message: 'My custom error message',
},
/[\d]/,
async value => value.length > 0,
async (value) => value.length > 0,
{
validator: value => true,
validator: (value) => true,
message: 'Some other error',
},
],
@ -282,8 +282,8 @@ const validators = {
const validators = {
username: async (s: string) => {
const taken = await API.isUsernameTaken(s)
return taken ? 'Username is taken': true
}
return taken ? 'Username is taken' : true
},
}
```
@ -307,7 +307,7 @@ const validators = {}
const options = {
setter: 'value', // This is not stricly necessarry as 'value' would already be the default.
getter: 'onChangeText',
extractor: text => text.toLowerCase(),
extractor: (text) => text.toLowerCase(),
}
export default () => {
@ -339,7 +339,7 @@ export default () => {
{...field('username', {
setter: 'value', // This is not stricly necessarry as 'value' would already be the default.
getter: 'onChangeText',
extractor: text => text.toLowerCase(),
extractor: (text) => text.toLowerCase(),
})}
/>
<Text>{form.username}</Text>

View File

@ -1,60 +1,62 @@
import * as React from 'react'
import { useEffect, useState } from 'react'
import { useMemo, useState } from 'react'
export type FieldOptions<G extends string = 'onChange', S extends string = 'value'> = {
extractor?: useFormExtractor
getter: G
setter: S
// Possible future ideas
// TODO: Scroll to error field
// TODO: Focus on error field
export type FieldOptions<G extends string = 'onChange', S extends string = 'value', T = any> = {
extractor?: useFormExtractor<T> | null
getter?: G
setter?: S
}
type RuleFunctionReturn = boolean | string
type RuleFunction<I> = (value: I) => RuleFunctionReturn | Promise<RuleFunctionReturn>
type Rule<I> = RuleFunction<I> | RegExp
type RuleObject<I> = Rule<I> | { rule: Rule<I>; message: string }
type RuleSet<I> = RuleObject<I> | RuleObject<I>[]
type RuleFunction<I, F> = (value: I, data: F) => RuleFunctionReturn | Promise<RuleFunctionReturn>
type Rule<I, F> = RuleFunction<I, F> | RegExp
type RuleObject<I, F> = Rule<I, F> | { rule: Rule<I, F>; message: string }
type RuleSet<I, F> = RuleObject<I, F> | RuleObject<I, F>[]
function isSimpleRule<I>(obj: RuleObject<I>): obj is Rule<I> {
function isSimpleRule<I, F>(obj: RuleObject<I, F>): obj is Rule<I, F> {
return obj instanceof RegExp || typeof obj === 'function'
}
export type useFormExtractor = (from: any) => any
export type useFormExtractor<T = any> = (from: any) => T
export const NoExtractor: useFormExtractor = (v: unknown) => v
export const HTMLInputExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.value
export const HTMLCheckboxExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.checked
export type FormOptions<R> = {
rules: R
// fields: FieldOptions
}
// Form = Type of form
// F = Type of form
// R = Rules, derived from F
// E = Errors, derived from F
export const useForm = <Form extends object, R extends { [K in keyof Form]?: RuleSet<Form[K]> }, E extends { [key in keyof R]?: RuleFunctionReturn }>(init: Form, options?: FormOptions<R>) => {
export const useForm = <F extends object, R extends { [K in keyof F]?: RuleSet<F[K], F> }, E extends { [key in keyof R]?: RuleFunctionReturn }>(init: F, options?: FormOptions<R>) => {
const validators: R = options?.rules ?? ({} as R)
const [form, setForm] = useState<Form>(init)
const [form, setForm] = useState<F>(init)
const [errors, setErrors] = useState<E>({} as E)
const [isValid, setIsValid] = useState<boolean>(true)
useEffect(() => {
setIsValid(!Object.values(errors).reduce((acc, cur) => acc || cur !== undefined, false))
const isValid = useMemo(() => {
return !Object.values(errors).reduce((acc, cur) => acc || cur !== undefined, false)
}, [errors])
const setField = <A extends keyof Form>(key: A, value: Form[A]) => {
const setField = <A extends keyof F>(key: A, value: F[A]) => {
setForm({
...form,
[key]: value,
})
}
async function applyRule<I>(value: any, rule: Rule<I>): Promise<RuleFunctionReturn> {
if (typeof rule === 'function') return await rule(value)
async function applyRule<I>(value: any, rule: Rule<I, F>): Promise<RuleFunctionReturn> {
if (typeof rule === 'function') return await rule(value, form)
if (rule instanceof RegExp) return rule.test(value)
throw new Error(`Unsupported validator: ${rule}`)
}
async function validate<K extends keyof Form>(key: K, value: Form[K]) {
const set: RuleSet<Form[K]> | undefined = validators[key] as any
async function validate<K extends keyof F>(key: K, value: F[K]) {
const set: RuleSet<F[K], F> | undefined = validators[key] as any
if (!set) return
const rules = Array.isArray(set) ? set : [set]
@ -74,18 +76,19 @@ export const useForm = <Form extends object, R extends { [K in keyof Form]?: Rul
})
}
function update<A extends keyof Form, RAW = any>(key: A, extractor?: (e: RAW) => Form[A]) {
return (value: RAW) => {
const extracted = extractor ? extractor(value) : HTMLInputExtractor(value)
// Internal use
function update<A extends keyof F>(key: A, extractor?: useFormExtractor<F[A]> | null) {
return (value: any) => {
const extracted = extractor ? extractor(value) : extractor === undefined ? HTMLInputExtractor(value) : value
setField(key, extracted)
validate(key, extracted)
}
}
type FieldReturn<K extends keyof Form, G extends string, S extends string> = { [getter in G]: ReturnType<typeof update<K>> } & { [setter in S]: Form[K] }
function field<K extends keyof Form>(key: K): FieldReturn<K, 'onChange', 'value'>
function field<K extends keyof Form, G extends string, S extends string>(key: K, opts: FieldOptions<G, S>): FieldReturn<K, G, S>
function field<K extends keyof Form, G extends string, S extends string>(key: K, opts?: FieldOptions<G, S>): FieldReturn<K, G, S> {
type FieldReturn<K extends keyof F, G extends string, S extends string> = { [getter in G]: ReturnType<typeof update<K>> } & { [setter in S]: F[K] }
function field<K extends keyof F>(key: K): FieldReturn<K, 'onChange', 'value'>
function field<K extends keyof F, G extends string, S extends string>(key: K, opts: FieldOptions<G, S, F[K]>): FieldReturn<K, G, S>
function field<K extends keyof F, G extends string, S extends string>(key: K, opts?: FieldOptions<G, S, F[K]>): FieldReturn<K, G, S> {
return {
[opts?.getter || 'onChange']: update<K>(key, opts?.extractor),
[opts?.setter || 'value']: form[key],

View File

@ -1,9 +1,12 @@
{
"name": "formhero",
"version": "0.1.0",
"version": "1.0.0-rc.0",
"type": "module",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"clean": "rm -rf ./dist",
"prepublishOnly": "run-s clean build test",
@ -21,16 +24,16 @@
},
"devDependencies": {
"@testing-library/react": "^13.4.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/coverage-c8": "^0.28.4",
"happy-dom": "^8.2.6",
"@vitest/coverage-c8": "^0.29.2",
"happy-dom": "^8.9.0",
"npm-run-all": "^4.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "^0.28.4"
"vite": "^4.1.4",
"vitest": "^0.29.2"
}
}

365
pnpm-lock.yaml generated
View File

@ -2,31 +2,31 @@ lockfileVersion: 5.4
specifiers:
'@testing-library/react': ^13.4.0
'@types/react': ^18.0.27
'@types/react-dom': ^18.0.10
'@types/react': ^18.0.28
'@types/react-dom': ^18.0.11
'@vitejs/plugin-react': ^3.1.0
'@vitest/coverage-c8': ^0.28.4
happy-dom: ^8.2.6
'@vitest/coverage-c8': ^0.29.2
happy-dom: ^8.9.0
npm-run-all: ^4.1.5
react: ^18.2.0
react-dom: ^18.2.0
typescript: ^4.9.5
vite: ^4.1.1
vitest: ^0.28.4
vite: ^4.1.4
vitest: ^0.29.2
devDependencies:
'@testing-library/react': 13.4.0_biqbaboplfbrettd7655fr4n2y
'@types/react': 18.0.27
'@types/react-dom': 18.0.10
'@vitejs/plugin-react': 3.1.0_vite@4.1.1
'@vitest/coverage-c8': 0.28.4_happy-dom@8.2.6
happy-dom: 8.2.6
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
'@vitejs/plugin-react': 3.1.0_vite@4.1.4
'@vitest/coverage-c8': 0.29.2_vitest@0.29.2
happy-dom: 8.9.0
npm-run-all: 4.1.5
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
typescript: 4.9.5
vite: 4.1.1
vitest: 0.28.4_happy-dom@8.2.6
vite: 4.1.4
vitest: 0.29.2_happy-dom@8.9.0
packages:
@ -45,25 +45,25 @@ packages:
'@babel/highlight': 7.18.6
dev: true
/@babel/compat-data/7.20.14:
resolution: {integrity: sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==}
/@babel/compat-data/7.21.0:
resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/core/7.20.12:
resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==}
/@babel/core/7.21.0:
resolution: {integrity: sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.14
'@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12
'@babel/helper-module-transforms': 7.20.11
'@babel/helpers': 7.20.13
'@babel/parser': 7.20.15
'@babel/generator': 7.21.1
'@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0
'@babel/helper-module-transforms': 7.21.2
'@babel/helpers': 7.21.0
'@babel/parser': 7.21.2
'@babel/template': 7.20.7
'@babel/traverse': 7.20.13
'@babel/types': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
convert-source-map: 1.9.0
debug: 4.3.4
gensync: 1.0.0-beta.2
@ -73,24 +73,25 @@ packages:
- supports-color
dev: true
/@babel/generator/7.20.14:
resolution: {integrity: sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==}
/@babel/generator/7.21.1:
resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
dev: true
/@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12:
/@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.0:
resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.20.14
'@babel/core': 7.20.12
'@babel/helper-validator-option': 7.18.6
'@babel/compat-data': 7.21.0
'@babel/core': 7.21.0
'@babel/helper-validator-option': 7.21.0
browserslist: 4.21.5
lru-cache: 5.1.1
semver: 6.3.0
@ -101,30 +102,30 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-function-name/7.19.0:
resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==}
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-module-transforms/7.20.11:
resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==}
/@babel/helper-module-transforms/7.21.2:
resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-environment-visitor': 7.18.9
@ -133,8 +134,8 @@ packages:
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
'@babel/template': 7.20.7
'@babel/traverse': 7.20.13
'@babel/types': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
dev: true
@ -148,14 +149,14 @@ packages:
resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/helper-string-parser/7.19.4:
@ -168,18 +169,18 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-option/7.18.6:
resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==}
/@babel/helper-validator-option/7.21.0:
resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helpers/7.20.13:
resolution: {integrity: sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==}
/@babel/helpers/7.21.0:
resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/traverse': 7.20.13
'@babel/types': 7.20.7
'@babel/traverse': 7.21.2
'@babel/types': 7.21.2
transitivePeerDependencies:
- supports-color
dev: true
@ -193,36 +194,36 @@ packages:
js-tokens: 4.0.0
dev: true
/@babel/parser/7.20.15:
resolution: {integrity: sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==}
/@babel/parser/7.21.2:
resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.20.7
'@babel/types': 7.21.2
dev: true
/@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.12:
resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==}
/@babel/plugin-transform-react-jsx-self/7.21.0_@babel+core@7.21.0:
resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.20.12
'@babel/core': 7.21.0
'@babel/helper-plugin-utils': 7.20.2
dev: true
/@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.12:
/@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.21.0:
resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.20.12
'@babel/core': 7.21.0
'@babel/helper-plugin-utils': 7.20.2
dev: true
/@babel/runtime/7.20.13:
resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==}
/@babel/runtime/7.21.0:
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
@ -233,30 +234,30 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.20.15
'@babel/types': 7.20.7
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
dev: true
/@babel/traverse/7.20.13:
resolution: {integrity: sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==}
/@babel/traverse/7.21.2:
resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.20.14
'@babel/generator': 7.21.1
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.20.15
'@babel/types': 7.20.7
'@babel/parser': 7.21.2
'@babel/types': 7.21.2
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types/7.20.7:
resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==}
/@babel/types/7.21.2:
resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
@ -514,7 +515,7 @@ packages:
engines: {node: '>=12'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/runtime': 7.20.13
'@babel/runtime': 7.21.0
'@types/aria-query': 5.0.1
aria-query: 5.1.3
chalk: 4.1.2
@ -530,9 +531,9 @@ packages:
react: ^18.0.0
react-dom: ^18.0.0
dependencies:
'@babel/runtime': 7.20.13
'@babel/runtime': 7.21.0
'@testing-library/dom': 8.20.0
'@types/react-dom': 18.0.10
'@types/react-dom': 18.0.11
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: true
@ -555,22 +556,22 @@ packages:
resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
dev: true
/@types/node/18.11.19:
resolution: {integrity: sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw==}
/@types/node/18.14.5:
resolution: {integrity: sha512-CRT4tMK/DHYhw1fcCEBwME9CSaZNclxfzVMe7GsO6ULSwsttbj70wSiX6rZdIjGblu93sTJxLdhNIT85KKI7Qw==}
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
dev: true
/@types/react-dom/18.0.10:
resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==}
/@types/react-dom/18.0.11:
resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
dependencies:
'@types/react': 18.0.27
'@types/react': 18.0.28
dev: true
/@types/react/18.0.27:
resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==}
/@types/react/18.0.28:
resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
@ -581,67 +582,57 @@ packages:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
dev: true
/@vitejs/plugin-react/3.1.0_vite@4.1.1:
/@vitejs/plugin-react/3.1.0_vite@4.1.4:
resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.1.0-beta.0
dependencies:
'@babel/core': 7.20.12
'@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.12
'@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12
'@babel/core': 7.21.0
'@babel/plugin-transform-react-jsx-self': 7.21.0_@babel+core@7.21.0
'@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.21.0
magic-string: 0.27.0
react-refresh: 0.14.0
vite: 4.1.1
vite: 4.1.4
transitivePeerDependencies:
- supports-color
dev: true
/@vitest/coverage-c8/0.28.4_happy-dom@8.2.6:
resolution: {integrity: sha512-btelLBxaWhHnywXRQxDlrvPhGdnuIaD3XulsxcZRIcnpLPbFu39dNTT0IYu2QWP2ZZrV0AmNtdLIfD4c77zMAg==}
/@vitest/coverage-c8/0.29.2_vitest@0.29.2:
resolution: {integrity: sha512-NmD3WirQCeQjjKfHu4iEq18DVOBFbLn9TKVdMpyi5YW2EtnS+K22/WE+9/wRrepOhyeTxuEFgxUVkCAE1GhbnQ==}
peerDependencies:
vitest: '>=0.29.0 <1'
dependencies:
c8: 7.12.0
c8: 7.13.0
picocolors: 1.0.0
std-env: 3.3.2
vitest: 0.28.4_happy-dom@8.2.6
transitivePeerDependencies:
- '@edge-runtime/vm'
- '@vitest/browser'
- '@vitest/ui'
- happy-dom
- jsdom
- less
- sass
- stylus
- sugarss
- supports-color
- terser
vitest: 0.29.2_happy-dom@8.9.0
dev: true
/@vitest/expect/0.28.4:
resolution: {integrity: sha512-JqK0NZ4brjvOSL8hXAnIsfi+jxDF7rH/ZWCGCt0FAqRnVFc1hXsfwXksQvEnKqD84avRt3gmeXoK4tNbmkoVsQ==}
/@vitest/expect/0.29.2:
resolution: {integrity: sha512-wjrdHB2ANTch3XKRhjWZN0UueFocH0cQbi2tR5Jtq60Nb3YOSmakjdAvUa2JFBu/o8Vjhj5cYbcMXkZxn1NzmA==}
dependencies:
'@vitest/spy': 0.28.4
'@vitest/utils': 0.28.4
'@vitest/spy': 0.29.2
'@vitest/utils': 0.29.2
chai: 4.3.7
dev: true
/@vitest/runner/0.28.4:
resolution: {integrity: sha512-Q8UV6GjDvBSTfUoq0QXVCNpNOUrWu4P2qvRq7ssJWzn0+S0ojbVOxEjMt+8a32X6SdkhF8ak+2nkppsqV0JyNQ==}
/@vitest/runner/0.29.2:
resolution: {integrity: sha512-A1P65f5+6ru36AyHWORhuQBJrOOcmDuhzl5RsaMNFe2jEkoj0faEszQS4CtPU/LxUYVIazlUtZTY0OEZmyZBnA==}
dependencies:
'@vitest/utils': 0.28.4
'@vitest/utils': 0.29.2
p-limit: 4.0.0
pathe: 1.1.0
dev: true
/@vitest/spy/0.28.4:
resolution: {integrity: sha512-8WuhfXLlvCXpNXEGJW6Gc+IKWI32435fQJLh43u70HnZ1otJOa2Cmg2Wy2Aym47ZnNCP4NolF+8cUPwd0MigKQ==}
/@vitest/spy/0.29.2:
resolution: {integrity: sha512-Hc44ft5kaAytlGL2PyFwdAsufjbdOvHklwjNy/gy/saRbg9Kfkxfh+PklLm1H2Ib/p586RkQeNFKYuJInUssyw==}
dependencies:
tinyspy: 1.0.2
tinyspy: 1.1.1
dev: true
/@vitest/utils/0.28.4:
resolution: {integrity: sha512-l2QztOLdc2LkR+w/lP52RGh8hW+Ul4KESmCAgVE8q737I7e7bQoAfkARKpkPJ4JQtGpwW4deqlj1732VZD7TFw==}
/@vitest/utils/0.29.2:
resolution: {integrity: sha512-F14/Uc+vCdclStS2KEoXJlOLAEyqRhnw0gM27iXw9bMTcyKRPJrQ+rlC6XZ125GIPvvKYMPpVxNhiou6PsEeYQ==}
dependencies:
cli-truncate: 3.1.0
diff: 5.1.0
@ -726,18 +717,14 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001450
electron-to-chromium: 1.4.286
node-releases: 2.0.9
caniuse-lite: 1.0.30001460
electron-to-chromium: 1.4.319
node-releases: 2.0.10
update-browserslist-db: 1.0.10_browserslist@4.21.5
dev: true
/buffer-from/1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
/c8/7.12.0:
resolution: {integrity: sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==}
/c8/7.13.0:
resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==}
engines: {node: '>=10.12.0'}
hasBin: true
dependencies:
@ -750,7 +737,7 @@ packages:
istanbul-reports: 3.1.5
rimraf: 3.0.2
test-exclude: 6.0.0
v8-to-istanbul: 9.0.1
v8-to-istanbul: 9.1.0
yargs: 16.2.0
yargs-parser: 20.2.9
dev: true
@ -767,8 +754,8 @@ packages:
get-intrinsic: 1.2.0
dev: true
/caniuse-lite/1.0.30001450:
resolution: {integrity: sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==}
/caniuse-lite/1.0.30001460:
resolution: {integrity: sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==}
dev: true
/chai/4.3.7:
@ -904,7 +891,7 @@ packages:
es-get-iterator: 1.1.3
get-intrinsic: 1.2.0
is-arguments: 1.1.1
is-array-buffer: 3.0.1
is-array-buffer: 3.0.2
is-date-object: 1.0.5
is-regex: 1.1.4
is-shared-array-buffer: 1.0.2
@ -919,8 +906,8 @@ packages:
which-typed-array: 1.1.9
dev: true
/define-properties/1.1.4:
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
/define-properties/1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
dependencies:
has-property-descriptors: 1.0.0
@ -940,8 +927,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
/electron-to-chromium/1.4.286:
resolution: {integrity: sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==}
/electron-to-chromium/1.4.319:
resolution: {integrity: sha512-WeoI6NwZUgteKB+Wmn692S35QycwwNxwgTomNnoCJ79znBAjtBi6C/cIW62JkXmpJRX5rKNYSLDBdAM8l5fH0w==}
dev: true
/emoji-regex/8.0.0:
@ -976,8 +963,8 @@ packages:
has-property-descriptors: 1.0.0
has-proto: 1.0.1
has-symbols: 1.0.3
internal-slot: 1.0.4
is-array-buffer: 3.0.1
internal-slot: 1.0.5
is-array-buffer: 3.0.2
is-callable: 1.2.7
is-negative-zero: 2.0.2
is-regex: 1.1.4
@ -1112,7 +1099,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
es-abstract: 1.21.1
functions-have-names: 1.2.3
dev: true
@ -1171,7 +1158,7 @@ packages:
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
engines: {node: '>= 0.4'}
dependencies:
define-properties: 1.1.4
define-properties: 1.2.0
dev: true
/gopd/1.0.1:
@ -1184,11 +1171,12 @@ packages:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
dev: true
/happy-dom/8.2.6:
resolution: {integrity: sha512-s53VwyMFpQPEZdN00M82i3tFTzz0T4kBVD4tu8b+im99s1NkLK6tfKGCCl2Jmf3ZWfFCRwS+DV2qkR7S1wmIhQ==}
/happy-dom/8.9.0:
resolution: {integrity: sha512-JZwJuGdR7ko8L61136YzmrLv7LgTh5b8XaEM3P709mLjyQuXJ3zHTDXvUtBBahRjGlcYW0zGjIiEWizoTUGKfA==}
dependencies:
css.escape: 1.5.1
he: 1.2.0
iconv-lite: 0.6.3
node-fetch: 2.6.9
webidl-conversions: 7.0.0
whatwg-encoding: 2.0.0
@ -1272,8 +1260,8 @@ packages:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/internal-slot/1.0.4:
resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==}
/internal-slot/1.0.5:
resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.0
@ -1289,8 +1277,8 @@ packages:
has-tostringtag: 1.0.0
dev: true
/is-array-buffer/3.0.1:
resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==}
/is-array-buffer/3.0.2:
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.0
@ -1544,13 +1532,13 @@ packages:
brace-expansion: 1.1.11
dev: true
/mlly/1.1.0:
resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==}
/mlly/1.1.1:
resolution: {integrity: sha512-Jnlh4W/aI4GySPo6+DyTN17Q75KKbLTyFK8BrGhjNP4rxuUjbRWhE6gHg3bs33URWAF44FRm7gdQA348i3XxRw==}
dependencies:
acorn: 8.8.2
pathe: 1.1.0
pkg-types: 1.0.1
ufo: 1.0.1
pkg-types: 1.0.2
ufo: 1.1.1
dev: true
/ms/2.1.2:
@ -1579,8 +1567,8 @@ packages:
whatwg-url: 5.0.0
dev: true
/node-releases/2.0.9:
resolution: {integrity: sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==}
/node-releases/2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
dev: true
/normalize-package-data/2.5.0:
@ -1617,7 +1605,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
dev: true
/object-keys/1.1.1:
@ -1630,7 +1618,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
has-symbols: 1.0.3
object-keys: 1.1.1
dev: true
@ -1724,11 +1712,11 @@ packages:
engines: {node: '>=4'}
dev: true
/pkg-types/1.0.1:
resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==}
/pkg-types/1.0.2:
resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==}
dependencies:
jsonc-parser: 3.2.0
mlly: 1.1.0
mlly: 1.1.1
pathe: 1.1.0
dev: true
@ -1794,7 +1782,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
functions-have-names: 1.2.3
dev: true
@ -1819,8 +1807,8 @@ packages:
glob: 7.2.3
dev: true
/rollup/3.14.0:
resolution: {integrity: sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==}
/rollup/3.18.0:
resolution: {integrity: sha512-J8C6VfEBjkvYPESMQYxKHxNOh4A5a3FlP+0BETGo34HEcE4eTlgCrO2+eWzlu2a/sHs2QUkZco+wscH7jhhgWg==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
@ -1912,13 +1900,6 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/source-map-support/0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
/source-map/0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@ -1958,7 +1939,7 @@ packages:
resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
engines: {node: '>= 0.4'}
dependencies:
internal-slot: 1.0.4
internal-slot: 1.0.5
dev: true
/string-width/4.2.3:
@ -1984,7 +1965,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
es-abstract: 1.21.1
dev: true
@ -1992,7 +1973,7 @@ packages:
resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
es-abstract: 1.21.1
dev: true
@ -2000,7 +1981,7 @@ packages:
resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
dependencies:
call-bind: 1.0.2
define-properties: 1.1.4
define-properties: 1.2.0
es-abstract: 1.21.1
dev: true
@ -2066,8 +2047,8 @@ packages:
engines: {node: '>=14.0.0'}
dev: true
/tinyspy/1.0.2:
resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==}
/tinyspy/1.1.1:
resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==}
engines: {node: '>=14.0.0'}
dev: true
@ -2099,8 +2080,8 @@ packages:
hasBin: true
dev: true
/ufo/1.0.1:
resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==}
/ufo/1.1.1:
resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==}
dev: true
/unbox-primitive/1.0.2:
@ -2123,8 +2104,8 @@ packages:
picocolors: 1.0.0
dev: true
/v8-to-istanbul/9.0.1:
resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==}
/v8-to-istanbul/9.1.0:
resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==}
engines: {node: '>=10.12.0'}
dependencies:
'@jridgewell/trace-mapping': 0.3.17
@ -2139,19 +2120,17 @@ packages:
spdx-expression-parse: 3.0.1
dev: true
/vite-node/0.28.4_@types+node@18.11.19:
resolution: {integrity: sha512-KM0Q0uSG/xHHKOJvVHc5xDBabgt0l70y7/lWTR7Q0pR5/MrYxadT+y32cJOE65FfjGmJgxpVEEY+69btJgcXOQ==}
/vite-node/0.29.2_@types+node@18.14.5:
resolution: {integrity: sha512-5oe1z6wzI3gkvc4yOBbDBbgpiWiApvuN4P55E8OI131JGrSuo4X3SOZrNmZYo4R8Zkze/dhi572blX0zc+6SdA==}
engines: {node: '>=v14.16.0'}
hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.4
mlly: 1.1.0
mlly: 1.1.1
pathe: 1.1.0
picocolors: 1.0.0
source-map: 0.6.1
source-map-support: 0.5.21
vite: 4.1.1_@types+node@18.11.19
vite: 4.1.4_@types+node@18.14.5
transitivePeerDependencies:
- '@types/node'
- less
@ -2162,8 +2141,8 @@ packages:
- terser
dev: true
/vite/4.1.1:
resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==}
/vite/4.1.4:
resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@ -2190,13 +2169,13 @@ packages:
esbuild: 0.16.17
postcss: 8.4.21
resolve: 1.22.1
rollup: 3.14.0
rollup: 3.18.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vite/4.1.1_@types+node@18.11.19:
resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==}
/vite/4.1.4_@types+node@18.14.5:
resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@ -2220,17 +2199,17 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 18.11.19
'@types/node': 18.14.5
esbuild: 0.16.17
postcss: 8.4.21
resolve: 1.22.1
rollup: 3.14.0
rollup: 3.18.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vitest/0.28.4_happy-dom@8.2.6:
resolution: {integrity: sha512-sfWIy0AdlbyGRhunm+TLQEJrFH9XuRPdApfubsyLcDbCRrUX717BRQKInTgzEfyl2Ipi1HWoHB84Nqtcwxogcg==}
/vitest/0.29.2_happy-dom@8.9.0:
resolution: {integrity: sha512-ydK9IGbAvoY8wkg29DQ4ivcVviCaUi3ivuPKfZEVddMTenFHUfB8EEDXQV8+RasEk1ACFLgMUqAaDuQ/Nk+mQA==}
engines: {node: '>=v14.16.0'}
hasBin: true
peerDependencies:
@ -2253,17 +2232,17 @@ packages:
dependencies:
'@types/chai': 4.3.4
'@types/chai-subset': 1.3.3
'@types/node': 18.11.19
'@vitest/expect': 0.28.4
'@vitest/runner': 0.28.4
'@vitest/spy': 0.28.4
'@vitest/utils': 0.28.4
'@types/node': 18.14.5
'@vitest/expect': 0.29.2
'@vitest/runner': 0.29.2
'@vitest/spy': 0.29.2
'@vitest/utils': 0.29.2
acorn: 8.8.2
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.7
debug: 4.3.4
happy-dom: 8.2.6
happy-dom: 8.9.0
local-pkg: 0.4.3
pathe: 1.1.0
picocolors: 1.0.0
@ -2272,9 +2251,9 @@ packages:
strip-literal: 1.0.1
tinybench: 2.3.1
tinypool: 0.3.1
tinyspy: 1.0.2
vite: 4.1.1_@types+node@18.11.19
vite-node: 0.28.4_@types+node@18.11.19
tinyspy: 1.1.1
vite: 4.1.4_@types+node@18.14.5
vite-node: 0.29.2_@types+node@18.14.5
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less

View File

@ -1,34 +1,15 @@
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
import React, { useEffect } from 'react'
import { act, cleanup, fireEvent, render } from '@testing-library/react'
import React from 'react'
import { beforeEach, describe, expect, test } from 'vitest'
import { useForm } from '../lib'
import { Insight, Util } from './shared'
beforeEach(cleanup)
const Insight = {
Portal({ data }: { data: any }) {
return <div data-testid="result">{JSON.stringify(data)}</div>
},
async verify(obj: any) {
const result = await screen.findByTestId('result')
const data = JSON.parse(result.innerText)
expect(data).toMatchObject(obj)
},
}
const Util = {
find<E extends HTMLElement = HTMLInputElement>(id: string) {
return screen.findByTestId<E>(id)
},
writeToField(node: HTMLInputElement, value: string) {
fireEvent.change(node, { target: { value } })
},
}
describe('Field', () => {
test('Basic Form', async () => {
const BasicForm = () => {
function Component() {
const form = useForm({ username: '', password: '' })
const { field } = form
return (
@ -47,7 +28,7 @@ describe('Field', () => {
)
}
render(<BasicForm />)
render(<Component />)
async function inputIntoForm(id: string, value: string) {
const node = await Util.find(id)
await act(() => {
@ -60,27 +41,65 @@ describe('Field', () => {
await inputIntoForm('password', 'bar')
})
test('setField', async () => {
const value = 'foo'
const Component = () => {
const { field, setField, form } = useForm({ username: '', password: '' })
useEffect(() => setField('username', value), [])
test.skip('Checkbox', async () => {
function Component() {
const { field, form } = useForm({ cool: false })
return (
<div>
<input data-testid="field" {...field('username')}></input>
<form>
<input
data-testid="field"
type="checkbox"
{...field('cool', {
setter: 'checked',
getter: 'onChange',
extractor: (e) => e.target.checked,
})}
/>
<Insight.Portal data={form} />
</div>
</form>
)
}
render(<Component />)
const node = await screen.findByTestId<HTMLInputElement>('field')
expect(node.value).toBe(value)
Insight.verify({ username: value, password: '' })
const field = await Util.find('field')
expect(field.checked).toBe(false)
await Insight.verify({ cool: false })
await act(() => {
// Bugged for now
fireEvent.click(field)
})
expect(field.checked).toBe(true)
await Insight.verify({ cool: true })
})
test('Select', async () => {
function Component() {
const { form, field } = useForm({ letter: '' })
return (
<>
<select data-testid="field" {...field('letter')}>
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<Insight.Portal data={form} />
</>
)
}
render(<Component />)
const field = await Util.find('field')
const value = 'b'
await act(() => {
fireEvent.change(field, { target: { value } })
})
expect(field.value).toBe(value)
await Insight.verify({ letter: value })
})
test('Field sync', async () => {
const value = 'foo'
const Component = () => {
function Component() {
const { field, form } = useForm({ name: '' })
return (
<form>
@ -101,54 +120,3 @@ describe('Field', () => {
expect(a.value).toBe(b.value)
})
})
describe('Validation', () => {
test('Basic', async () => {
const Component = () => {
const { errors, field } = useForm({ password: '' }, { rules: { password: [(p) => p.length > 8] } })
return (
<div>
<input {...field('password')} data-testid="field" />
<Insight.Portal data={errors} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '123')
})
Insight.verify({ password: true })
})
test('Array of rules', async () => {
const Component = () => {
const { errors, field } = useForm({ password: '' }, { rules: { password: [(p) => p.length > 8, /#/] } })
return (
<div>
<input {...field('password')} data-testid="field" />
<Insight.Portal data={errors} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '12345678')
})
Insight.verify({ password: true })
await act(() => {
Util.writeToField(node, '1234#5678')
})
Insight.verify({})
})
})
// Is valid
// Reset / setForm
// Set error
// Checkbox
// Extractor
// Custom extractor

11
test/blocks.tsx Normal file
View File

@ -0,0 +1,11 @@
import React from 'react'
// Custom field with non standard setter and getter. Emulate custom component from a library
export function NumberField(props: { number: number; update: (value: number) => void }) {
return <input data-testid="field" value={props.number} onChange={(e) => props.update(parseInt(e.target.value))} />
}
// Component that needs a different extractor, as it's returning the actual value and not the event.
export function DirectReturnInput(props: { value: string; onChange: (v: string) => void }) {
return <input data-testid="field" value={props.value} onChange={(e) => props.onChange(e.target.value)} />
}

57
test/options.test.tsx Normal file
View File

@ -0,0 +1,57 @@
import { act, cleanup, render } from '@testing-library/react'
import React from 'react'
import { beforeEach, describe, test } from 'vitest'
import { useForm } from '../lib'
import { DirectReturnInput, NumberField } from './blocks'
import { Insight, Util } from './shared'
beforeEach(cleanup)
describe('Options', () => {
test('Custom component props', async () => {
function Component() {
const { form, field } = useForm({ foo: 5 })
return (
<div>
<NumberField
{...field('foo', {
setter: 'number',
getter: 'update',
extractor: null,
})}
/>
<Insight.Portal data={form} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '123')
})
Insight.verify({ foo: 123 })
})
test('Disable default extractor', async () => {
function Component() {
const { form, field } = useForm({ username: '' })
return (
<div>
<DirectReturnInput {...field('username', { extractor: null })} />
<Insight.Portal data={form} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '123')
})
Insight.verify({ username: '123' })
})
})

23
test/shared.tsx Normal file
View File

@ -0,0 +1,23 @@
import { fireEvent, screen } from '@testing-library/react'
import React from 'react'
import { expect } from 'vitest'
export const Insight = {
Portal({ data }: { data: any }) {
return <div data-testid="result">{JSON.stringify(data)}</div>
},
async verify(obj: any) {
const result = await screen.findByTestId('result')
const data = JSON.parse(result.innerText)
expect(data).toMatchObject(obj)
},
}
export const Util = {
find<E extends HTMLElement = HTMLInputElement>(id: string) {
return screen.findByTestId<E>(id)
},
writeToField(node: HTMLInputElement, value: string) {
fireEvent.change(node, { target: { value } })
},
}

53
test/utility.test.tsx Normal file
View File

@ -0,0 +1,53 @@
import { cleanup, render } from '@testing-library/react'
import React, { useEffect } from 'react'
import { beforeEach, describe, expect, test } from 'vitest'
import { useForm } from '../lib'
import { Insight, Util } from './shared'
beforeEach(cleanup)
describe('Utility', () => {
test('Manually set a single field', async () => {
const value = 'foo'
function Component() {
const { field, setField, form } = useForm({ username: '', password: '' })
useEffect(() => setField('username', value), [])
return (
<div>
<input data-testid="field" {...field('username')}></input>
<Insight.Portal data={form} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
expect(node.value).toBe(value)
Insight.verify({ username: value, password: '' })
})
test('Manually set the form state later on', async () => {
const value = 'foo'
function Component() {
const { form, field, setForm } = useForm({ username: '' })
useEffect(() => {
setForm({
username: value,
})
}, [])
return (
<form>
<input data-testid="username" {...field('username')} />
<Insight.Portal data={form} />
</form>
)
}
render(<Component />)
const node = await Util.find('username')
expect(node.value).toBe(value)
await Insight.verify({ username: value })
})
})

109
test/validation.test.tsx Normal file
View File

@ -0,0 +1,109 @@
import { act, cleanup, fireEvent, render } from '@testing-library/react'
import React from 'react'
import { beforeEach, describe, test } from 'vitest'
import { useForm } from '../lib'
import { Insight, Util } from './shared'
beforeEach(cleanup)
describe('Validation', () => {
test('Basic', async () => {
function Component() {
const { errors, field } = useForm({ password: '' }, { rules: { password: [(p) => p.length > 8] } })
return (
<div>
<input {...field('password')} data-testid="field" />
<Insight.Portal data={errors} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '123')
})
Insight.verify({ password: true })
})
test('Array of rules', async () => {
function Component() {
const { errors, field } = useForm({ password: '' }, { rules: { password: [(p) => p.length > 8, /#/] } })
return (
<div>
<input {...field('password')} data-testid="field" />
<Insight.Portal data={errors} />
</div>
)
}
render(<Component />)
const node = await Util.find('field')
await act(() => {
Util.writeToField(node, '12345678')
})
Insight.verify({ password: true })
await act(() => {
Util.writeToField(node, '1234#5678')
})
Insight.verify({})
})
// https://github.com/testing-library/react-testing-library/issues/828
test.skip('Invalid rule', async () => {
function Component() {
const { field } = useForm(
{ username: '' },
{
rules: {
username: [
// @ts-ignore Give an invalid rules and expect to fail
5,
],
},
}
)
return (
<form>
<input data-testid="field" {...field('username')} />
</form>
)
}
render(<Component />)
const field = await Util.find('field')
await act(() => {
Util.writeToField(field, 'abc')
})
})
test('Invalid dependency on other component', async () => {
function Component() {
const { errors, field } = useForm(
{ min: 10, max: 20 },
{
rules: {
max: (value, form) => value > form.min,
},
}
)
return (
<form>
<input type="number" {...field('min')} />
<input type="number" {...field('max')} data-testid="max" />
<Insight.Portal data={errors} />
</form>
)
}
render(<Component />)
const field = await Util.find('max')
const value = 5
await act(() => {
fireEvent.change(field, { target: { value } })
})
await Insight.verify({ max: true })
})
})