@homebound/form-state 2.15.3 → 2.15.4

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.
@@ -101,7 +101,7 @@ function newValueFieldState(parentInstance, parentState, key, rules, isIdKey, is
101
101
  }
102
102
  // If the user has deleted/emptied a value that was originally set, keep it as `null`
103
103
  // so that our partial update to the backend correctly unsets it.
104
- const keepNull = !utils_1.isEmpty(this.originalValue) && utils_1.isEmpty(value);
104
+ const keepNull = !utils_1.isEmpty(this.originalValue) && utils_1.isEmpty(value) && !opts.refreshing;
105
105
  // If a list of primitives was originally undefined, coerce `[]` to `undefined`
106
106
  const coerceEmptyList = value && value instanceof Array && value.length === 0 && utils_1.isEmpty(this.originalValue);
107
107
  const newValue = keepNull ? null : utils_1.isEmpty(value) || coerceEmptyList ? undefined : value;
@@ -135,7 +135,9 @@ function newValueFieldState(parentInstance, parentState, key, rules, isIdKey, is
135
135
  },
136
136
  get originalValue() {
137
137
  // A dummy check to for reactivity around our non-proxy value
138
- return _originalValueTick.value > -1 ? _originalValue : _originalValue;
138
+ const value = _originalValueTick.value > -1 ? _originalValue : _originalValue;
139
+ // Re-create the `keepNull` logic so that `.value` === `.originalValue`
140
+ return value === null ? undefined : value;
139
141
  },
140
142
  set originalValue(v) {
141
143
  _originalValue = v;
@@ -91,6 +91,12 @@ function useFormState(opts) {
91
91
  return;
92
92
  }
93
93
  setLoading(form, opts);
94
+ // Note: maybe someday we want to watch the `id` value and notice if it's changing (i.e. 2 -> 3),
95
+ // and treat this more as a reset than a refresh, b/c the id changing means it's probably
96
+ // not "the cache refreshed after a mutation save" but "the cache changed b/c of changing
97
+ // rows in the table that our side panel's form has open/is focused on" (which ideally would
98
+ // be treated as a full component remount by having an `key` field somewhere in the parent
99
+ // component, but it's unlikely the user will always remember to do this).
94
100
  form.set(utils_1.initValue(config, init), { refreshing: true });
95
101
  }, [form, ...dep]);
96
102
  // Use useEffect so that we don't touch the form.init proxy during a render
@@ -528,6 +528,41 @@ describe("useFormState", () => {
528
528
  expect(result.current.autoSave.status).toBe(AutoSaveStatusProvider_1.AutoSaveStatus.DONE);
529
529
  jest.clearAllTimers();
530
530
  });
531
+ it("treats the id changing as a whole new entity instead of a delete", async () => {
532
+ const config = { firstName: { type: "value" } };
533
+ function TestComponent() {
534
+ // Given an initial author a1
535
+ const [author, setAuthor] = react_2.useState({ id: "a:1", firstName: "a1" });
536
+ const form = useFormState_1.useFormState({ config, init: { input: author, map: (a) => a } });
537
+ return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "value" }, { children: String(form.firstName.value) }), void 0),
538
+ jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "dirty" }, { children: String(form.firstName.dirty) }), void 0),
539
+ jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "originalValue" }, { children: String(form.firstName.originalValue) }), void 0),
540
+ jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "objectValue" }, { children: String(form.value.firstName) }), void 0),
541
+ jsx_runtime_1.jsx("div", { "data-testid": "a1", onClick: () => setAuthor({ id: "a:1", firstName: "a1" }) }, void 0),
542
+ jsx_runtime_1.jsx("div", { "data-testid": "a2", onClick: () => setAuthor({ id: "a:2", firstName: undefined }) }, void 0)] }, void 0)) }, void 0));
543
+ }
544
+ const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
545
+ // And the value is initially a1/and not dirty
546
+ expect(r.value()).toHaveTextContent("a1");
547
+ expect(r.dirty()).toHaveTextContent("false");
548
+ expect(r.originalValue()).toHaveTextContent("a1");
549
+ expect(r.objectValue()).toHaveTextContent("a1");
550
+ // When we switch to a completely separate author
551
+ rtl_utils_1.click(r.a2);
552
+ // Then it switches to the next author
553
+ expect(r.value()).toHaveTextContent("undefined");
554
+ // And the field is not dirty (which had been the case before this bug fix)
555
+ expect(r.dirty()).toHaveTextContent("false");
556
+ expect(r.originalValue()).toHaveTextContent("undefined");
557
+ expect(r.objectValue()).toHaveTextContent("undefined");
558
+ // And when we switch back to the original author
559
+ rtl_utils_1.click(r.a1);
560
+ // It again restores the value and does not think an edit was WIP
561
+ expect(r.value()).toHaveTextContent("a1");
562
+ expect(r.dirty()).toHaveTextContent("false");
563
+ expect(r.originalValue()).toHaveTextContent("a1");
564
+ expect(r.objectValue()).toHaveTextContent("a1");
565
+ });
531
566
  });
532
567
  const authorConfig = {
533
568
  id: { type: "value" },
package/dist/utils.d.ts CHANGED
@@ -35,7 +35,7 @@ export declare function isEmpty(value: any): boolean;
35
35
  *
36
36
  * - "plain" objects that have no custom prototype/i.e. are object literals
37
37
  * - objects that implement `toJSON`
38
- *
38
+ * - arrays
39
39
  */
40
40
  export declare function areEqual<T>(a?: T, b?: T, strictOrder?: boolean): boolean;
41
41
  export declare function hasToJSON(o?: unknown): o is {
package/dist/utils.js CHANGED
@@ -100,7 +100,7 @@ exports.isEmpty = isEmpty;
100
100
  *
101
101
  * - "plain" objects that have no custom prototype/i.e. are object literals
102
102
  * - objects that implement `toJSON`
103
- *
103
+ * - arrays
104
104
  */
105
105
  function areEqual(a, b, strictOrder) {
106
106
  if (is_plain_object_1.isPlainObject(a)) {
@@ -117,7 +117,7 @@ function areEqual(a, b, strictOrder) {
117
117
  }
118
118
  if (a.length !== b.length)
119
119
  return false;
120
- return a.every((a1) => b.some((b1) => fast_deep_equal_1.default(a1, b1)));
120
+ return a.every((a1) => b.some((b1) => areEqual(a1, b1)));
121
121
  }
122
122
  return a === b;
123
123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/form-state",
3
- "version": "2.15.3",
3
+ "version": "2.15.4",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",