@homebound/form-state 2.30.0 → 2.30.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.
@@ -127,7 +127,10 @@ function newValueFieldState(parentCopy, parentInstance, parentState, key, rules,
127
127
  // is exactly acking our change, or
128
128
  // - the user hasn't made any changes since `.changedValue` was put on the wire, i.e. the
129
129
  // server received our value, but wanted to change it.
130
- const keepLocalWipChange = this.value !== value && !isAckingUnset && !acceptServerAck;
130
+ // Compare by content (not `!==`) so array/list-of-primitive values are treated as acked when
131
+ // the server returns the same contents in a new array instance (otherwise the field would
132
+ // stay dirty forever, since reference equality never holds for a fresh array).
133
+ const keepLocalWipChange = !(0, utils_1.areEqual)(this.value, value, strictOrder) && !isAckingUnset && !acceptServerAck;
131
134
  if (keepLocalWipChange)
132
135
  return;
133
136
  }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ export { type ListFieldState } from "./fields/listField";
4
4
  export { createObjectState, type Fragment, type ObjectState, fragment } from "./fields/objectField";
5
5
  export { type FieldState, type ValueAdapter } from "./fields/valueField";
6
6
  export { required, type Rule } from "./rules";
7
- export { useFormState } from "./useFormState";
7
+ export { type FormObjectState, useFormState } from "./useFormState";
8
8
  export { useFormStates } from "./useFormStates";
9
9
  export { type ObjectConfig } from "./config";
@@ -286,6 +286,28 @@ describe("useFormState", () => {
286
286
  return { firstName: input.legalFirstName, lastName: input.legalLastName };
287
287
  }
288
288
  });
289
+ it("becomes clean when update acks an array value with the same contents", async () => {
290
+ const config = { colors: { type: "value" } };
291
+ function TestComponent() {
292
+ const form = (0, useFormState_1.useFormState)({ config, init: { input: { colors: ["red", "green", "blue"] } } });
293
+ // Note: we deliberately do not read `form.changedValue` (the way an auto-save would), so the only
294
+ // thing that can let `update` accept the server value is content equality, not the changedValue hint.
295
+ return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => {
296
+ var _a;
297
+ return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "colors", children: ((_a = form.colors.value) !== null && _a !== void 0 ? _a : []).join(",") }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "dirty", children: String(form.dirty) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "edit", onClick: () => form.colors.set(["red", "green"]) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "saveAndUpdate", onClick: () => form.update({ colors: ["red", "green"] }) })] }));
298
+ } }));
299
+ }
300
+ const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
301
+ expect(r.dirty.textContent).toEqual("false");
302
+ // When the user edits the array
303
+ (0, rtl_utils_1.click)(r.edit);
304
+ expect(r.dirty.textContent).toEqual("true");
305
+ // And the server acks the same contents in a brand-new array instance
306
+ (0, rtl_utils_1.click)(r.saveAndUpdate);
307
+ // Then the form is clean (we don't get stuck dirty on array reference inequality)
308
+ expect(r.colors.textContent).toEqual("red,green");
309
+ expect(r.dirty.textContent).toEqual("false");
310
+ });
289
311
  it("can accept new data while read only", async () => {
290
312
  // Given a component
291
313
  function TestComponent() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/form-state",
3
- "version": "2.30.0",
3
+ "version": "2.30.1",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",