@playfast/reform-forms 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@playfast/reform-forms",
3
3
  "playbook": "./playbook",
4
- "version": "1.0.1",
4
+ "version": "1.1.1",
5
5
  "type": "module",
6
6
  "description": "Headless, schema-driven form state for reform — values, validation, field limitations, list operations, and submit flow, rendering nothing.",
7
7
  "keywords": [
package/src/form.test.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { expect, test } from 'vitest'
2
- import { Effect, Layer, ManagedRuntime, Option, Schema as S } from 'effect'
3
- import { Engine } from '@playfast/reform'
4
- import * as Form from './form'
1
+ import { expect, it } from "@effect/vitest";
2
+ import { Effect, Layer, Option, Schema as S } from "effect";
3
+ import { Engine } from "@playfast/reform";
4
+ import * as Form from "./form";
5
5
 
6
6
  const waitUntil = <A, R>(
7
7
  read: Effect.Effect<A, never, R>,
@@ -11,82 +11,81 @@ const waitUntil = <A, R>(
11
11
  Effect.flatMap(read, (value) =>
12
12
  pred(value) || rounds <= 0
13
13
  ? Effect.succeed(value)
14
- : Effect.flatMap(Effect.yieldNow(), () => waitUntil(read, pred, rounds - 1)),
15
- )
14
+ : Effect.flatMap(Effect.yieldNow(), () =>
15
+ waitUntil(read, pred, rounds - 1),
16
+ ),
17
+ );
16
18
 
17
19
  const CheckoutSchema = S.Struct({
18
20
  email: S.String,
19
21
  items: S.Array(S.Struct({ name: S.String, qty: S.Number })),
20
22
  payment: S.Union(
21
- S.TaggedStruct('Card', { cardNumber: S.String }),
22
- S.TaggedStruct('Paypal', { email: S.String }),
23
+ S.TaggedStruct("Card", { cardNumber: S.String }),
24
+ S.TaggedStruct("Paypal", { email: S.String }),
23
25
  ),
24
- })
26
+ });
25
27
 
26
- class CheckoutForm extends Form.make('CheckoutForm', { schema: CheckoutSchema }) {}
28
+ class CheckoutForm extends Form.make("CheckoutForm", {
29
+ schema: CheckoutSchema,
30
+ }) {}
27
31
 
28
32
  const layer = Form.live(CheckoutForm, {
29
33
  initial: {
30
- email: '',
34
+ email: "",
31
35
  items: [],
32
- payment: { _tag: 'Card', cardNumber: '' },
36
+ payment: { _tag: "Card", cardNumber: "" },
33
37
  },
34
38
  validate: ({ values }) =>
35
- values.email.includes('@') ? undefined : Form.error('email', 'Email must contain @'),
36
- }).pipe(Layer.provideMerge(Engine))
39
+ values.email.includes("@")
40
+ ? undefined
41
+ : Form.error("email", "Email must contain @"),
42
+ }).pipe(Layer.provideMerge(Engine));
37
43
 
38
- test('field bindings write through the form reducer', async () => {
39
- const runtime = ManagedRuntime.make(layer)
40
- try {
41
- await runtime.runPromise(
42
- Effect.gen(function* () {
43
- const view = yield* Form.view(CheckoutForm)
44
- view.field('email').set('a@example.com')
44
+ it.scopedLive("field bindings write through the form reducer", () =>
45
+ Effect.gen(function* () {
46
+ const view = yield* Form.view(CheckoutForm);
47
+ view.field("email").set("a@example.com");
45
48
 
46
- const next = yield* waitUntil(Form.view(CheckoutForm), (v) => v.values.email === 'a@example.com')
47
- expect(next.field('email').dirty).toBe(true)
48
- }),
49
- )
50
- } finally {
51
- await runtime.dispose()
52
- }
53
- })
49
+ const next = yield* waitUntil(
50
+ Form.view(CheckoutForm),
51
+ (v) => v.values.email === "a@example.com",
52
+ );
53
+ expect(next.field("email").dirty).toBe(true);
54
+ }).pipe(Effect.provide(layer)),
55
+ );
54
56
 
55
- test('array bindings append and remove items', async () => {
56
- const runtime = ManagedRuntime.make(layer)
57
- try {
58
- await runtime.runPromise(
59
- Effect.gen(function* () {
60
- const view = yield* Form.view(CheckoutForm)
61
- view.array('items').append({ name: 'milk', qty: 1 })
57
+ it.scopedLive("array bindings append and remove items", () =>
58
+ Effect.gen(function* () {
59
+ const view = yield* Form.view(CheckoutForm);
60
+ view.array("items").append({ name: "milk", qty: 1 });
62
61
 
63
- const withItem = yield* waitUntil(Form.view(CheckoutForm), (v) => v.values.items.length === 1)
64
- expect(withItem.array('items').items[0]?.key).toMatch(/^form-item-/)
62
+ const withItem = yield* waitUntil(
63
+ Form.view(CheckoutForm),
64
+ (v) => v.values.items.length === 1,
65
+ );
66
+ expect(withItem.array("items").items[0]?.key).toMatch(/^form-item-/);
65
67
 
66
- withItem.array('items').remove(0)
67
- const empty = yield* waitUntil(Form.view(CheckoutForm), (v) => v.values.items.length === 0)
68
- expect(empty.values.items).toEqual([])
69
- }),
70
- )
71
- } finally {
72
- await runtime.dispose()
73
- }
74
- })
68
+ withItem.array("items").remove(0);
69
+ const empty = yield* waitUntil(
70
+ Form.view(CheckoutForm),
71
+ (v) => v.values.items.length === 0,
72
+ );
73
+ expect(empty.values.items).toEqual([]);
74
+ }).pipe(Effect.provide(layer)),
75
+ );
75
76
 
76
- test('submit routes custom validation errors to fields', async () => {
77
- const runtime = ManagedRuntime.make(layer)
78
- try {
79
- await runtime.runPromise(
80
- Effect.gen(function* () {
81
- const view = yield* Form.view(CheckoutForm)
82
- view.submit()
77
+ it.scopedLive("submit routes custom validation errors to fields", () =>
78
+ Effect.gen(function* () {
79
+ const view = yield* Form.view(CheckoutForm);
80
+ view.submit();
83
81
 
84
- const invalid = yield* waitUntil(Form.view(CheckoutForm), (v) => v.validationCount > 0)
85
- const error = invalid.field('email').error
86
- expect(Option.isSome(error) ? error.value : undefined).toBe('Email must contain @')
87
- }),
88
- )
89
- } finally {
90
- await runtime.dispose()
91
- }
92
- })
82
+ const invalid = yield* waitUntil(
83
+ Form.view(CheckoutForm),
84
+ (v) => v.validationCount > 0,
85
+ );
86
+ const error = invalid.field("email").error;
87
+ expect(Option.isSome(error) ? error.value : undefined).toBe(
88
+ "Email must contain @",
89
+ );
90
+ }).pipe(Effect.provide(layer)),
91
+ );