Skip to content
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
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { createForm } from "./factory"
export { createErrorsMap } from "./utils"
export { useForm, useField } from "./react-hooks"
export {
AnyFormValues,
Expand All @@ -13,5 +14,5 @@ export {
ValidationEvent,
ValidationResult,
FormValues,
Form
Form,
} from "./types"
79 changes: 79 additions & 0 deletions src/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createEvent } from "effector"
import { createField } from "./field"
import { createErrorsMap } from "./utils"
import { ValidationError } from "./types"

test("createErrorsMap with field with errors", () => {
const field = createField("email", {
init: "value",
})
const addError = createEvent<ValidationError>()
const resetErrors = createEvent<void>()
field.$errors.on(addError, (errors, error) => [...errors, error])
field.$errors.on(resetErrors, () => [])

const error = {
rule: "email",
value: "value",
}
const error2 = {
rule: "email2",
value: "value",
}

const errorsMap = createErrorsMap({ email: field })
expect(errorsMap.getState()).toEqual({ email: [] })

addError(error)
addError(error2)

expect(errorsMap.getState()).toEqual({ email: [error, error2] })

resetErrors()
expect(errorsMap.getState()).toEqual({ email: [] })
})

test("createErrorsMap with multiple fields", () => {
const field1 = createField("email", {
init: "value",
})
const field2 = createField("password", {
init: "value",
})

const addField1Error = createEvent<ValidationError>()
const addField2Error = createEvent<ValidationError>()

const resetErrors = createEvent<void>()
field1.$errors.on(addField1Error, (errors, error) => [...errors, error])
field2.$errors.on(addField2Error, (errors, error) => [...errors, error])
field1.$errors.on(resetErrors, () => [])
field2.$errors.on(resetErrors, () => [])

const errorsMap = createErrorsMap({ email: field1, password: field2 })

expect(errorsMap.getState()).toEqual({ email: [], password: [] })

const field1Error = {
rule: "email",
value: "value",
}
const field2Error = {
rule: "password",
value: "value",
}

addField1Error(field1Error)
addField1Error(field1Error)
addField1Error(field1Error)
addField1Error(field1Error)
addField2Error(field2Error)

expect(errorsMap.getState()).toEqual({
email: [field1Error, field1Error, field1Error, field1Error],
password: [field2Error],
})

resetErrors()
expect(errorsMap.getState()).toEqual({ email: [], password: [] })
})
21 changes: 21 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { combine, Store, StoreWritable } from "effector"
import { AnyFormValues, FormFields, ValidationError } from "./types"

type ErrorsMap<Values extends AnyFormValues> = {
[K in keyof Values]: ValidationError<Values[K]>[]
}

export function createErrorsMap<Values extends AnyFormValues>(
fields: FormFields<Values>
): Store<ErrorsMap<Values>> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const shape: Record<string, StoreWritable<ValidationError<any>[]>> = {}

for (const fieldName in fields) {
// eslint-disable-next-line no-prototype-builtins
if (!fields.hasOwnProperty(fieldName)) continue
shape[fieldName] = fields[fieldName].$errors
}

return combine(shape) as Store<ErrorsMap<Values>>
}