@homebound/form-state 2.26.10 → 2.26.12
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/fields/listField.js +14 -4
- package/dist/formState.test.js +74 -0
- package/package.json +1 -1
package/dist/fields/listField.js
CHANGED
|
@@ -208,6 +208,8 @@ parentInstance, parentState, key, rules, listConfig, config, strictOrder, maybeA
|
|
|
208
208
|
//
|
|
209
209
|
// Only do this if one of our client-side objects is missing an id
|
|
210
210
|
const incomingByHash = idKey && currentItems.some((item) => !item[idKey]) && (0, utils_1.groupBy)(incomingItems, hashWithoutId);
|
|
211
|
+
// Watch for deletions
|
|
212
|
+
const hasOpKey = Object.keys(listConfig.config).includes("op");
|
|
211
213
|
// First, process all current items
|
|
212
214
|
for (const currentItem of currentItems) {
|
|
213
215
|
const childState = rowMap.get(currentItem); // We'll always have a child state for `currentItems`
|
|
@@ -221,12 +223,20 @@ parentInstance, parentState, key, rules, listConfig, config, strictOrder, maybeA
|
|
|
221
223
|
// Once matched, we don't this to be an addedRow anymore
|
|
222
224
|
addedRows.delete(currentItem);
|
|
223
225
|
}
|
|
224
|
-
else if (childState.dirty
|
|
225
|
-
//
|
|
226
|
-
|
|
226
|
+
else if (!childState.dirty && !addedRows.has(currentItem)) {
|
|
227
|
+
// Local is not dirty/added, and it's not upstream, so let it get removed
|
|
228
|
+
}
|
|
229
|
+
else if (hasOpKey && currentItem.op === "delete") {
|
|
230
|
+
// We were locally marked as deleted, and not finding a match is the server acking that we're gone
|
|
231
|
+
}
|
|
232
|
+
else if (currentItems.length === incomingItems.length &&
|
|
233
|
+
!!idKey &&
|
|
234
|
+
currentItem[idKey] === undefined) {
|
|
235
|
+
// Assume our newly-assigned id is coming back
|
|
227
236
|
}
|
|
228
237
|
else {
|
|
229
|
-
//
|
|
238
|
+
// If no incoming, but we're dirty (see above) and not deleted (see above), keep the WIP change
|
|
239
|
+
mergedItems.push(currentItem);
|
|
230
240
|
}
|
|
231
241
|
}
|
|
232
242
|
// Then, add any incoming items that don't exist in current
|
package/dist/formState.test.js
CHANGED
|
@@ -205,6 +205,80 @@ describe("formState", () => {
|
|
|
205
205
|
// And we're not longer dirty
|
|
206
206
|
expect(state.books.dirty).toBe(false);
|
|
207
207
|
});
|
|
208
|
+
it("list value can refresh and not drop new children that are just data", () => {
|
|
209
|
+
const config = configBuilders_1.f.config({
|
|
210
|
+
firstName: configBuilders_1.f.value(),
|
|
211
|
+
tags: configBuilders_1.f.list({ combo: configBuilders_1.f.value() }),
|
|
212
|
+
});
|
|
213
|
+
const state = (0, objectField_1.createObjectState)(config, { firstName: "a1", tags: [] });
|
|
214
|
+
// And we add a new child on the client-side (and it doesn't have an id)
|
|
215
|
+
state.tags.add({ combo: ["b1", "b2"] });
|
|
216
|
+
// When we refresh it with the original list undefined
|
|
217
|
+
state.set({ tags: [] }, { refreshing: true });
|
|
218
|
+
// Then it keeps the new child
|
|
219
|
+
expect(state.tags.value).toEqual([{ combo: ["b1", "b2"] }]);
|
|
220
|
+
// And it's still considered changed
|
|
221
|
+
expect(state.tags.dirty).toBe(true);
|
|
222
|
+
expect(state.tags.originalValue).toEqual([]);
|
|
223
|
+
// And we add an additional child
|
|
224
|
+
state.tags.add({ combo: ["c1", "c2"] });
|
|
225
|
+
// And a refresh brings back the 1st child acked from the server
|
|
226
|
+
state.set({ tags: [{ combo: ["b1", "b2"] }] }, { refreshing: true });
|
|
227
|
+
// Then we still have 2 children
|
|
228
|
+
expect(state.tags.value).toEqual([{ combo: ["b1", "b2"] }, { combo: ["c1", "c2"] }]);
|
|
229
|
+
// And we're still dirty
|
|
230
|
+
expect(state.tags.dirty).toBe(true);
|
|
231
|
+
// When the server finally acks both children
|
|
232
|
+
state.set({ tags: [{ combo: ["b1", "b2"] }, { combo: ["c1", "c2"] }] }, { refreshing: true });
|
|
233
|
+
// Then we're finally clean
|
|
234
|
+
expect(state.tags.dirty).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
it("list value can refresh and drop children that were deleted", () => {
|
|
237
|
+
// Given a list field that is currently populated
|
|
238
|
+
const a1 = {
|
|
239
|
+
id: "a:1",
|
|
240
|
+
firstName: "a1",
|
|
241
|
+
books: [
|
|
242
|
+
{ id: "b:1", title: "b1", op: "include" },
|
|
243
|
+
{ id: "b:2", title: "b2", op: "include" },
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
// And incremental deletion is enabled
|
|
247
|
+
const config = configBuilders_1.f.config({
|
|
248
|
+
id: configBuilders_1.f.value(),
|
|
249
|
+
books: configBuilders_1.f.list({ id: configBuilders_1.f.value(), title: configBuilders_1.f.value(), op: configBuilders_1.f.value() }),
|
|
250
|
+
});
|
|
251
|
+
const state = (0, objectField_1.createObjectState)(config, a1);
|
|
252
|
+
// When we delete the 1st book
|
|
253
|
+
state.books.rows[0].op.set("delete");
|
|
254
|
+
// When we refresh it only the 2nd book
|
|
255
|
+
state.set({ books: [{ id: "b:2", title: "b2" }] }, { refreshing: true });
|
|
256
|
+
// Then we only have 1 book
|
|
257
|
+
expect(state.books.value).toEqual([{ id: "b:2", title: "b2", op: "include" }]);
|
|
258
|
+
// And we're not dirty
|
|
259
|
+
expect(state.books.dirty).toBe(false);
|
|
260
|
+
});
|
|
261
|
+
it("list value can refresh and merge children that were added", () => {
|
|
262
|
+
// Given a list field that is currently populated with 1 child
|
|
263
|
+
const a1 = { id: "a:1", books: [{ id: "b:1", title: "b1" }] };
|
|
264
|
+
const state = createAuthorInputState(a1);
|
|
265
|
+
// When we add a new book
|
|
266
|
+
state.books.add({ title: "b2" });
|
|
267
|
+
// When we refresh it with both books, and the 2nd book now has its id assigned
|
|
268
|
+
state.set({
|
|
269
|
+
books: [
|
|
270
|
+
{ id: "b:1", title: "b1" },
|
|
271
|
+
{ id: "b:2", title: "b2" },
|
|
272
|
+
],
|
|
273
|
+
}, { refreshing: true });
|
|
274
|
+
// Then wes till have two books
|
|
275
|
+
expect(state.books.value).toEqual([
|
|
276
|
+
{ id: "b:1", title: "b1" },
|
|
277
|
+
{ id: "b:2", title: "b2" },
|
|
278
|
+
]);
|
|
279
|
+
// And we're not dirty
|
|
280
|
+
expect(state.books.dirty).toBe(false);
|
|
281
|
+
});
|
|
208
282
|
it("list value can observe changes", () => {
|
|
209
283
|
const b1 = { title: "t1" };
|
|
210
284
|
const a1 = { firstName: "a1", books: [b1] };
|