-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
170 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./ListField"; | ||
export * from "./useListFieldActions"; | ||
export * from "./listFieldBuilder"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { expectTypeOf, test } from "vitest"; | ||
|
||
import { listFieldBuilder } from "./listFieldBuilder"; | ||
import { textField } from "../../fields"; | ||
|
||
test("listFieldBuilder - cannot build with random data", () => { | ||
const addressBuilder = listFieldBuilder(({ street }) => ({ | ||
street: textField({ name: "street", value: street }), | ||
})); | ||
|
||
expectTypeOf(addressBuilder).toBeCallableWith([{ street: "foo" }]); | ||
|
||
// Doesnt work for no-argument (due to function overload) | ||
// https://github.com/mmkal/expect-type/issues/30 | ||
// expectTypeOf(addressBuilder).toBeCallableWith(); | ||
|
||
// TODO: expect-type issue | ||
// expectTypeOf(addressBuilder).not.toBeCallableWith([{ notStreet: "foo" }]); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { renderHook } from "@testing-library/react"; | ||
import { formAtom, useFormValues } from "form-atoms"; | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { listFieldBuilder } from "./listFieldBuilder"; | ||
import { textField } from "../../fields"; | ||
|
||
describe("listFieldBuilder()", () => { | ||
describe("building plain atoms", () => { | ||
const builder = listFieldBuilder((value) => | ||
textField({ name: "street", value }), | ||
); | ||
|
||
it("initializes empty atom when called without argument", async () => { | ||
const field = builder(); | ||
|
||
const form = formAtom({ test: field }); | ||
const { result } = renderHook(() => useFormValues(form)); | ||
|
||
expect(result.current).toEqual({ test: "" }); | ||
}); | ||
|
||
it("initialized multiple items", async () => { | ||
const streets = builder(["foo", "bar"]); | ||
|
||
const form = formAtom({ streets }); | ||
const { result } = renderHook(() => useFormValues(form)); | ||
|
||
expect(result.current).toEqual({ streets: ["foo", "bar"] }); | ||
}); | ||
}); | ||
|
||
describe("building form fields object", () => { | ||
const addressBuilder = listFieldBuilder(({ street, city }) => ({ | ||
street: textField({ name: "street", value: street }), | ||
city: textField({ name: "city", value: city }), | ||
})); | ||
|
||
it("initializes empty form fields when called without argument", async () => { | ||
const fields = addressBuilder(); | ||
|
||
const form = formAtom(fields); | ||
const { result } = renderHook(() => useFormValues(form)); | ||
|
||
expect(result.current).toEqual({ street: "", city: "" }); | ||
}); | ||
|
||
it("initialized multiple items", async () => { | ||
const addresses = addressBuilder([ | ||
{ city: "Kosice", street: "Hlavna" }, | ||
{ city: "Bratislava", street: "Hrad" }, | ||
]); | ||
|
||
const form = formAtom({ addresses }); | ||
const { result } = renderHook(() => useFormValues(form)); | ||
|
||
expect(result.current).toEqual({ | ||
addresses: [ | ||
{ city: "Kosice", street: "Hlavna" }, | ||
{ city: "Bratislava", street: "Hrad" }, | ||
], | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { FieldAtom, FormFieldValues, FormFields } from "form-atoms"; | ||
import { ExtractAtomValue } from "jotai"; | ||
|
||
type FieldAtomValue<T extends FieldAtom<any>> = ExtractAtomValue< | ||
ExtractAtomValue<T>["value"] | ||
>; | ||
|
||
type ListFieldItems = FieldAtom<any> | FormFields; | ||
|
||
type ListFieldValue<T extends ListFieldItems> = T extends FieldAtom<any> | ||
? FieldAtomValue<T> | ||
: T extends FormFields | ||
? FormFieldValues<T> | ||
: never; | ||
|
||
// actual type must be one of overloads, as this one is ignored | ||
export function listFieldBuilder< | ||
Fields extends ListFieldItems, | ||
Value = ListFieldValue<Fields>, | ||
>(builder: (value: Value) => Fields) { | ||
let emptyValue: undefined | Value = undefined; | ||
try { | ||
// test if builder is 'atomBuilder', e.g. returns plain atom | ||
// @ts-expect-error this is a test call | ||
builder(undefined); | ||
} catch { | ||
// builder is 'fieldsBuilder', e.g. it returns Record<string, fieldAtom> | ||
emptyValue = {} as Value; | ||
} | ||
|
||
function buildFields(): Fields; | ||
function buildFields(data: ListFieldValue<Fields>[]): Fields[]; | ||
function buildFields(data?: ListFieldValue<Fields>[]) { | ||
if (data) { | ||
return data.map(builder); | ||
} else { | ||
// @ts-expect-error empty call | ||
return builder(emptyValue); | ||
} | ||
} | ||
|
||
return buildFields; | ||
} |