@homebound/form-state 2.2.6 → 2.2.7

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/dist/formState.js CHANGED
@@ -329,7 +329,7 @@ function newValueFieldState(parentInstance, parentState, key, rules, isIdKey, is
329
329
  if (this.readOnly && !opts.resetting && !opts.refreshing) {
330
330
  throw new Error(`${key} is currently readOnly`);
331
331
  }
332
- if (opts.refreshing && this.dirty) {
332
+ if (opts.refreshing && this.dirty && value !== this.value) {
333
333
  // Ignore refreshed values if we're already dirty
334
334
  return;
335
335
  }
@@ -411,9 +411,18 @@ function newListFieldState(parentInstance, parentState, key, rules, listConfig,
411
411
  },
412
412
  // private
413
413
  hasNewItems() {
414
- const currentList = this.value;
415
- const a = (currentList || []).every((e) => (originalCopy || []).includes(e));
416
- const b = (originalCopy || []).every((e) => (currentList || []).includes(e));
414
+ const [current, original] = [this.value || [], originalCopy || []];
415
+ // Instead of relying on just object identities, we look up each child's state
416
+ // in rowMap, because we already dedup/check object identity (i.e. look for id fields)
417
+ // when create object states.
418
+ const a = current.every((e) => {
419
+ const state = rowMap.get(e);
420
+ return original.some((e) => rowMap.get(e) === state);
421
+ });
422
+ const b = original.every((e) => {
423
+ const state = rowMap.get(e);
424
+ return current.some((e) => rowMap.get(e) === state);
425
+ });
417
426
  const isSame = a && b;
418
427
  return !isSame;
419
428
  },
@@ -496,6 +505,10 @@ function newListFieldState(parentInstance, parentState, key, rules, listConfig,
496
505
  // Return the already-observable'd value so that our `parent.value[key] = values` doesn't re-proxy things
497
506
  return childState.value;
498
507
  });
508
+ // Reset originalCopy so that our dirty checks have the right # of rows.
509
+ if (opts.refreshing) {
510
+ originalCopy = [...(parentInstance[key] || [])];
511
+ }
499
512
  _tick.value++;
500
513
  },
501
514
  add(value, spliceIndex) {
@@ -1197,6 +1197,18 @@ describe("formState", () => {
1197
1197
  { id: "b:3", title: "b3" },
1198
1198
  ],
1199
1199
  };
1200
+ // Eventually our local values are saved
1201
+ const data3 = {
1202
+ id: "a:1",
1203
+ firstName: "local",
1204
+ lastName: "l2",
1205
+ address: { id: "address:1", street: "local", city: "c2" },
1206
+ books: [
1207
+ { id: "b:1", title: "local" },
1208
+ { id: "b:2", title: "b2" },
1209
+ { id: "b:3", title: "b3" },
1210
+ ],
1211
+ };
1200
1212
  // And we start out with data1
1201
1213
  const [data, setData] = react_1.useState(data1);
1202
1214
  const form = formState_1.useFormState({ config, init: { input: data, map: (d) => d } });
@@ -1205,9 +1217,6 @@ describe("formState", () => {
1205
1217
  form.address.street.value = "local";
1206
1218
  form.books.rows[0].title.value = "local";
1207
1219
  }
1208
- function refreshData() {
1209
- setData(data2);
1210
- }
1211
1220
  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": "firstName" }, { children: form.firstName.value }), void 0),
1212
1221
  jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "lastName" }, { children: form.lastName.value }), void 0),
1213
1222
  jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "street" }, { children: form.address.street.value }), void 0),
@@ -1216,7 +1225,8 @@ describe("formState", () => {
1216
1225
  jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "title2" }, { children: form.books.rows[1].title.value }), void 0),
1217
1226
  jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "booksLength" }, { children: form.books.rows.length }), void 0),
1218
1227
  jsx_runtime_1.jsx("button", { "data-testid": "makeLocalChanges", onClick: makeLocalChanges }, void 0),
1219
- jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: refreshData }, void 0),
1228
+ jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0),
1229
+ jsx_runtime_1.jsx("button", { "data-testid": "saveData", onClick: () => setData(data3) }, void 0),
1220
1230
  jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "changedValue" }, { children: JSON.stringify(form.changedValue) }), void 0)] }, void 0)) }, void 0));
1221
1231
  }
1222
1232
  // And we start out with the initial query data
@@ -1253,6 +1263,12 @@ describe("formState", () => {
1253
1263
  books: [{ id: "b:1", title: "local" }, { id: "b:2" }, { id: "b:3" }],
1254
1264
  firstName: "local",
1255
1265
  });
1266
+ // And then when our mutation results come back
1267
+ rtl_utils_1.click(r.saveData);
1268
+ // Then changedValue doesn't show our local changes anymore
1269
+ expect(JSON.parse(r.changedValue().textContent)).toEqual({
1270
+ id: "a:1",
1271
+ });
1256
1272
  });
1257
1273
  it("useFormState can accept new data while read only", async () => {
1258
1274
  // Given a component
@@ -1332,8 +1348,8 @@ describe("formState", () => {
1332
1348
  expect(r.firstName().textContent).toEqual("f2");
1333
1349
  expect(r.fullName().textContent).toEqual("f2 l2");
1334
1350
  });
1335
- it("can trigger onBlur for fields in list that were initially undefined", async () => {
1336
- const onBlur = jest.fn();
1351
+ it("can trigger auto save for fields in list that were initially undefined", async () => {
1352
+ const autoSave = jest.fn();
1337
1353
  // Given a component
1338
1354
  function TestComponent() {
1339
1355
  // When the data is initially undefined
@@ -1342,27 +1358,30 @@ describe("formState", () => {
1342
1358
  const form = formState_1.useFormState({
1343
1359
  config: authorWithBooksConfig,
1344
1360
  init: { input: data, map: (d) => d, ifUndefined: { books: [] } },
1345
- autoSave: () => onBlur(),
1361
+ autoSave,
1346
1362
  });
1347
1363
  return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0),
1348
1364
  jsx_runtime_1.jsx("button", { "data-testid": "add", onClick: () => form.books.add({ title: "New Book" }) }, void 0),
1349
- jsx_runtime_1.jsx("button", { "data-testid": "blur", onClick: () => form.books.rows[0].title.blur() }, void 0),
1350
- jsx_runtime_1.jsx("button", { "data-testid": "blurNew", onClick: () => form.books.rows[1].title.blur() }, void 0)] }, void 0)) }, void 0));
1365
+ jsx_runtime_1.jsx("button", { "data-testid": "blurBookOne", onClick: () => form.books.rows[0].title.blur() }, void 0),
1366
+ jsx_runtime_1.jsx("button", { "data-testid": "blurBookTwo", onClick: () => form.books.rows[1].title.blur() }, void 0)] }, void 0)) }, void 0));
1351
1367
  }
1352
1368
  // Given a formState with `onBlur` set
1353
1369
  const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
1354
1370
  // When the data/child is now available
1355
1371
  rtl_utils_1.click(r.refreshData);
1356
1372
  // And the field is blurred
1357
- rtl_utils_1.click(r.blur);
1358
- // Then expect onBlur to triggered
1359
- expect(onBlur).toBeCalledTimes(1);
1373
+ rtl_utils_1.click(r.blurBookOne);
1374
+ // Then we don't auto-save because nothing has changed
1375
+ expect(autoSave).toBeCalledTimes(0);
1360
1376
  // And when adding a new book
1361
1377
  rtl_utils_1.click(r.add);
1378
+ // We autoSave the new row right away (because we don't have any validation rules
1379
+ // that say the new row can't be empty)
1380
+ expect(autoSave).toBeCalledTimes(1);
1362
1381
  // And the new book is blurred
1363
- rtl_utils_1.click(r.blurNew);
1364
- // Then expect onBlur to be triggered again
1365
- expect(onBlur).toBeCalledTimes(3);
1382
+ rtl_utils_1.click(r.blurBookTwo);
1383
+ // Then we auto save again
1384
+ expect(autoSave).toBeCalledTimes(2);
1366
1385
  });
1367
1386
  });
1368
1387
  class ObservableObject {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/form-state",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",