@homebound/form-state 2.26.10 → 2.26.11
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 +9 -2
- package/dist/formState.test.js +53 -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`
|
|
@@ -222,8 +224,13 @@ parentInstance, parentState, key, rules, listConfig, config, strictOrder, maybeA
|
|
|
222
224
|
addedRows.delete(currentItem);
|
|
223
225
|
}
|
|
224
226
|
else if (childState.dirty || addedRows.has(currentItem)) {
|
|
225
|
-
// If no incoming, but we're dirty, keep the WIP change
|
|
226
|
-
|
|
227
|
+
// If no incoming, but we're dirty (...and not deleted), keep the WIP change
|
|
228
|
+
if (hasOpKey && currentItem.op === "delete") {
|
|
229
|
+
// delete
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
mergedItems.push(currentItem);
|
|
233
|
+
}
|
|
227
234
|
}
|
|
228
235
|
else {
|
|
229
236
|
// Local is not dirty, and it's not upstream, so remove it
|
package/dist/formState.test.js
CHANGED
|
@@ -205,6 +205,59 @@ 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
|
+
});
|
|
208
261
|
it("list value can observe changes", () => {
|
|
209
262
|
const b1 = { title: "t1" };
|
|
210
263
|
const a1 = { firstName: "a1", books: [b1] };
|