@homebound/form-state 2.22.0 → 2.22.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.
@@ -965,12 +965,14 @@ describe("formState", () => {
965
965
  const formState = (0, objectField_1.createObjectState)({
966
966
  firstName: { type: "value" },
967
967
  lastName: { type: "value" },
968
- fullName: { type: "value", computed: true },
968
+ fullName: { type: "value" },
969
969
  }, new ObservableObject());
970
970
  expect(formState.firstName.value).toEqual("first");
971
971
  expect(formState.fullName.value).toEqual("first last");
972
+ expect(formState.fullName.dirty).toEqual(false);
972
973
  formState.firstName.value = "change";
973
974
  expect(formState.fullName.value).toEqual("change last");
975
+ expect(formState.fullName.dirty).toEqual(true);
974
976
  formState.revertChanges();
975
977
  expect(formState.firstName.value).toEqual("first");
976
978
  });
@@ -244,9 +244,11 @@ describe("useFormState", () => {
244
244
  // Given a component
245
245
  // And it's using a class/mobx proxy as the basis for the data
246
246
  class AuthorRow {
247
- constructor(firstName, lastName) {
247
+ constructor(firstName, lastName, books) {
248
248
  this.firstName = firstName;
249
249
  this.lastName = lastName;
250
+ this.books = books;
251
+ (0, mobx_1.makeAutoObservable)(this);
250
252
  }
251
253
  get fullName() {
252
254
  return this.firstName + " " + this.lastName;
@@ -254,27 +256,32 @@ describe("useFormState", () => {
254
256
  }
255
257
  function TestComponent() {
256
258
  // And we have two sets of data
257
- const data1 = { firstName: "f1", lastName: "l1" };
258
- const data2 = { firstName: "f2", lastName: "l2" };
259
+ const data1 = { firstName: "f1", lastName: "l1", books: [] };
260
+ const data2 = { firstName: "f2", lastName: "l2", books: [{ title: "t1" }] };
259
261
  // And we start out with data1
260
262
  const [data, setData] = (0, react_2.useState)(data1);
261
- const author = (0, react_2.useMemo)(() => new AuthorRow(data.firstName, data.lastName), [data]);
263
+ const author = (0, react_2.useMemo)(() => new AuthorRow(data.firstName, data.lastName, data.books), [data]);
262
264
  const config = (0, react_2.useMemo)(() => ({
263
265
  firstName: { type: "value" },
264
266
  lastName: { type: "value" },
265
267
  fullName: { type: "value", computed: true },
268
+ books: { type: "list", config: { title: { type: "value" } } },
266
269
  }), []);
267
270
  const form = (0, useFormState_1.useFormState)({ config, init: { input: author, map: (a) => a } });
268
- return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "fullName", children: form.fullName.value }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) })] })) }));
271
+ return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "fullName", children: form.fullName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "booksLength", children: form.books.rows.length }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "booksDirty", children: String(form.books.touched) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) })] })) }));
269
272
  }
270
273
  // And we start out with the initial query data
271
274
  const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
272
275
  expect(r.firstName.textContent).toEqual("f1");
273
276
  expect(r.fullName.textContent).toEqual("f1 l1");
277
+ expect(r.booksLength.textContent).toEqual("0");
278
+ expect(r.booksDirty.textContent).toEqual("false");
274
279
  // When the new query is ran i.e. due to a cache refresh
275
280
  (0, rtl_utils_1.click)(r.refreshData);
276
281
  expect(r.firstName.textContent).toEqual("f2");
277
282
  expect(r.fullName.textContent).toEqual("f2 l2");
283
+ expect(r.booksLength.textContent).toEqual("1");
284
+ expect(r.booksDirty.textContent).toEqual("false");
278
285
  });
279
286
  it("can trigger auto save for fields in list that were initially undefined", async () => {
280
287
  const autoSave = jest.fn();
package/dist/utils.js CHANGED
@@ -133,7 +133,7 @@ function deepClone(obj, map = new WeakMap()) {
133
133
  return map.get(obj);
134
134
  const result = Array.isArray(obj) ? [] : {};
135
135
  map.set(obj, result);
136
- Object.assign(result, ...Object.keys(obj).map((key) => ({ [key]: deepClone(obj[key], map) })));
136
+ Object.assign(result, ...getAllPropertyNames(obj).map((key) => ({ [key]: deepClone(obj[key], map) })));
137
137
  return result;
138
138
  }
139
139
  else {
@@ -141,3 +141,10 @@ function deepClone(obj, map = new WeakMap()) {
141
141
  }
142
142
  }
143
143
  exports.deepClone = deepClone;
144
+ /** Returns all property names, including mobx computeds (non-enumerable) & inherited. */
145
+ function getAllPropertyNames(obj) {
146
+ const proto = Object.getPrototypeOf(obj);
147
+ // Don't crawl up into Object.prototype, or into arrays/observable arrays
148
+ const inherited = proto && proto !== Object.prototype && !Array.isArray(obj) ? getAllPropertyNames(proto) : [];
149
+ return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/form-state",
3
- "version": "2.22.0",
3
+ "version": "2.22.1",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",