@arrai-innovations/reactive-helpers 2.5.2 → 2.6.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.
- package/package.json +1 -1
- package/tests/unit/use/listInstance.spec.js +58 -6
- package/use/listInstance.js +41 -14
- package/use/listSort.js +4 -8
- package/use/listSubscription.js +1 -1
- package/use/objectSubscription.js +1 -1
- package/utils/getFakeId.js +7 -7
- package/utils/index.js +1 -0
- package/utils/loadingCombine.js +1 -1
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { expectErrorToBeNull } from "../expectHelpers";
|
|
2
|
-
import { isReactive, nextTick, reactive } from "vue";
|
|
3
|
-
import { keyBy } from "lodash";
|
|
4
1
|
import flushPromises from "flush-promises";
|
|
2
|
+
import { keyBy } from "lodash";
|
|
5
3
|
import { inspect } from "util";
|
|
4
|
+
import { isReactive, nextTick, reactive } from "vue";
|
|
5
|
+
import { expectErrorToBeNull } from "../expectHelpers";
|
|
6
6
|
|
|
7
7
|
afterAll(() => {
|
|
8
8
|
jest.restoreAllMocks();
|
|
@@ -98,7 +98,7 @@ describe("use/listInstance.spec.js", function () {
|
|
|
98
98
|
expectErrorToBeNull(listInstance.state.error);
|
|
99
99
|
expect(listInstance.state.errored).toBe(false);
|
|
100
100
|
expect(listInstance.state.loading).toBe(true);
|
|
101
|
-
expect({ ...listInstance.state.
|
|
101
|
+
expect({ ...listInstance.state.objects }).toEqual({});
|
|
102
102
|
|
|
103
103
|
await nextTick();
|
|
104
104
|
|
|
@@ -196,7 +196,7 @@ describe("use/listInstance.spec.js", function () {
|
|
|
196
196
|
expectErrorToBeNull(listInstance.state.error);
|
|
197
197
|
expect(listInstance.state.errored).toBe(false);
|
|
198
198
|
expect(listInstance.state.loading).toBe(true);
|
|
199
|
-
expect({ ...listInstance.state.
|
|
199
|
+
expect({ ...listInstance.state.objects }).toEqual({});
|
|
200
200
|
|
|
201
201
|
await nextTick();
|
|
202
202
|
|
|
@@ -250,7 +250,7 @@ describe("use/listInstance.spec.js", function () {
|
|
|
250
250
|
expectErrorToBeNull(listInstance.state.error);
|
|
251
251
|
expect(listInstance.state.errored).toBe(false);
|
|
252
252
|
expect(listInstance.state.loading).toBe(true);
|
|
253
|
-
expect({ ...listInstance.state.
|
|
253
|
+
expect({ ...listInstance.state.objects }).toEqual({});
|
|
254
254
|
|
|
255
255
|
await nextTick();
|
|
256
256
|
|
|
@@ -285,6 +285,58 @@ describe("use/listInstance.spec.js", function () {
|
|
|
285
285
|
});
|
|
286
286
|
expect(globalList).toHaveBeenCalledTimes(1);
|
|
287
287
|
});
|
|
288
|
+
it("clearList should empty the list", async function () {
|
|
289
|
+
const listArgs = reactive({
|
|
290
|
+
user: 1,
|
|
291
|
+
});
|
|
292
|
+
const retrieveArgs = reactive({
|
|
293
|
+
fields,
|
|
294
|
+
});
|
|
295
|
+
const listInstance = useListInstance({
|
|
296
|
+
listArgs,
|
|
297
|
+
retrieveArgs,
|
|
298
|
+
});
|
|
299
|
+
let crudListResolve;
|
|
300
|
+
const crudListPromise = new Promise((resolve) => {
|
|
301
|
+
crudListResolve = resolve;
|
|
302
|
+
});
|
|
303
|
+
let passedPageCallback;
|
|
304
|
+
globalList.mockImplementation(({ pageCallback }) => {
|
|
305
|
+
passedPageCallback = pageCallback;
|
|
306
|
+
return crudListPromise;
|
|
307
|
+
});
|
|
308
|
+
const liListResolve = listInstance.list();
|
|
309
|
+
passedPageCallback(crudListResolvedPage1);
|
|
310
|
+
passedPageCallback(crudListResolvedPage2);
|
|
311
|
+
crudListResolve();
|
|
312
|
+
await flushPromises();
|
|
313
|
+
|
|
314
|
+
await expect(liListResolve).resolves.toBe(true);
|
|
315
|
+
|
|
316
|
+
expectErrorToBeNull(listInstance.state.error);
|
|
317
|
+
expect(listInstance.state.errored).toBe(false);
|
|
318
|
+
expect(listInstance.state.loading).toBe(false);
|
|
319
|
+
expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
|
|
320
|
+
await flushPromises();
|
|
321
|
+
expect(listInstance.state.order).toEqual(Object.keys(crudListResolvedObjects));
|
|
322
|
+
expect(listInstance.state.objectsInOrder).toEqual(Object.values(crudListResolvedObjects));
|
|
323
|
+
expect(globalList).toHaveBeenCalledWith({
|
|
324
|
+
crudArgs: { stream: "test_stream" },
|
|
325
|
+
listArgs: { user: 1 },
|
|
326
|
+
retrieveArgs: { fields: fields },
|
|
327
|
+
pageCallback: passedPageCallback,
|
|
328
|
+
});
|
|
329
|
+
expect(globalList).toHaveBeenCalledTimes(1);
|
|
330
|
+
|
|
331
|
+
listInstance.clearList();
|
|
332
|
+
expectErrorToBeNull(listInstance.state.error);
|
|
333
|
+
expect(listInstance.state.errored).toBe(false);
|
|
334
|
+
expect(listInstance.state.loading).toBe(false);
|
|
335
|
+
expect({ ...listInstance.state.objects }).toEqual({});
|
|
336
|
+
await flushPromises();
|
|
337
|
+
expect(listInstance.state.order).toEqual([]);
|
|
338
|
+
expect(listInstance.state.objectsInOrder).toEqual([]);
|
|
339
|
+
});
|
|
288
340
|
});
|
|
289
341
|
it("useListInstances", async function () {
|
|
290
342
|
const listInstanceA = useListInstance({
|
package/use/listInstance.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import inspect from "browser-util-inspect";
|
|
1
2
|
import { cloneDeep } from "lodash";
|
|
2
3
|
import { computed, effectScope, reactive } from "vue";
|
|
3
4
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
5
|
import { getFakeId } from "../utils/getFakeId";
|
|
5
|
-
import inspect from "browser-util-inspect";
|
|
6
6
|
|
|
7
7
|
export class ListError extends Error {
|
|
8
8
|
constructor(message, code) {
|
|
@@ -31,6 +31,40 @@ export function useListInstances(listInstanceArgs) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} }) {
|
|
34
|
+
// ### touching the _objectsMap or _objectsProxy directly will not trigger reactivity ###
|
|
35
|
+
const _objectsMap = new Map();
|
|
36
|
+
// ### touching the _objectsMap or _objectsProxy directly will not trigger reactivity ###
|
|
37
|
+
const _objectsProxy = new Proxy(_objectsMap, {
|
|
38
|
+
get(target, prop) {
|
|
39
|
+
return target.get(prop); // map's get not Reflect.get
|
|
40
|
+
},
|
|
41
|
+
has(target, prop) {
|
|
42
|
+
return target.has(prop); // map's has not Reflect.has
|
|
43
|
+
},
|
|
44
|
+
set(target, prop, value) {
|
|
45
|
+
target.set(prop, value); // map.set() returns the map, we don't need that
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
ownKeys(target) {
|
|
49
|
+
return [...target.keys()]; // map.keys() returns an iterator, we need an array
|
|
50
|
+
},
|
|
51
|
+
deleteProperty(target, p) {
|
|
52
|
+
target.delete(p); // map.delete() returns false if the key didn't exist
|
|
53
|
+
return true; // objects return true regardless of whether the property existed
|
|
54
|
+
},
|
|
55
|
+
getOwnPropertyDescriptor(target, p) {
|
|
56
|
+
const value = target.get(p);
|
|
57
|
+
return value
|
|
58
|
+
? {
|
|
59
|
+
configurable: true,
|
|
60
|
+
enumerable: true,
|
|
61
|
+
value,
|
|
62
|
+
writable: true,
|
|
63
|
+
}
|
|
64
|
+
: Reflect.getOwnPropertyDescriptor(target, p); // we can't report target properties as non-existent re: proxy invariants
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
// ### touching the _objectsMap or _objectsProxy directly will not trigger reactivity ###
|
|
34
68
|
const state = reactive({
|
|
35
69
|
crud: {
|
|
36
70
|
args: {},
|
|
@@ -38,11 +72,10 @@ export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} })
|
|
|
38
72
|
},
|
|
39
73
|
retrieveArgs,
|
|
40
74
|
listArgs,
|
|
41
|
-
objects:
|
|
75
|
+
objects: _objectsProxy,
|
|
42
76
|
loading: undefined,
|
|
43
77
|
errored: false,
|
|
44
78
|
error: null,
|
|
45
|
-
order: [],
|
|
46
79
|
});
|
|
47
80
|
// prevent linking of all instances to the same default .args object
|
|
48
81
|
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
@@ -110,10 +143,7 @@ export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} })
|
|
|
110
143
|
if (object.id in state.objects) {
|
|
111
144
|
throw new ListError(`addListObject: list already has object for id: ${inspect(object.id)}`, "duplicate-id");
|
|
112
145
|
}
|
|
113
|
-
state.objects[object.id] =
|
|
114
|
-
// objects keys are always strings.
|
|
115
|
-
state.order.push(`${object.id}`);
|
|
116
|
-
assignReactiveObject(state.objects[object.id], object);
|
|
146
|
+
state.objects[object.id] = object;
|
|
117
147
|
}
|
|
118
148
|
|
|
119
149
|
function updateListObject(object) {
|
|
@@ -136,16 +166,12 @@ export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} })
|
|
|
136
166
|
"missing-object"
|
|
137
167
|
);
|
|
138
168
|
}
|
|
139
|
-
// objects keys are always strings.
|
|
140
|
-
state.order.splice(state.order.indexOf(`${objectId}`), 1);
|
|
141
169
|
delete state.objects[objectId];
|
|
142
170
|
}
|
|
143
171
|
|
|
144
172
|
function clearList() {
|
|
145
|
-
state.
|
|
146
|
-
|
|
147
|
-
delete state.objects[item];
|
|
148
|
-
}
|
|
173
|
+
assignReactiveObject(state.objects, {});
|
|
174
|
+
clearError();
|
|
149
175
|
}
|
|
150
176
|
|
|
151
177
|
function ourGetFakeId() {
|
|
@@ -160,7 +186,8 @@ export function useListInstance({ crudArgs, listArgs = {}, retrieveArgs = {} })
|
|
|
160
186
|
const es = effectScope();
|
|
161
187
|
|
|
162
188
|
es.run(() => {
|
|
163
|
-
state.objectsInOrder = computed(() =>
|
|
189
|
+
state.objectsInOrder = computed(() => Object.values(state.objects));
|
|
190
|
+
state.order = computed(() => Object.keys(state.objects));
|
|
164
191
|
});
|
|
165
192
|
|
|
166
193
|
const returnedObject = {
|
package/use/listSort.js
CHANGED
|
@@ -105,17 +105,12 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
|
|
|
105
105
|
|
|
106
106
|
function sortWatch() {
|
|
107
107
|
if (!state.orderByRules || !state.orderByRules.length) {
|
|
108
|
-
|
|
109
|
-
assignReactiveObject(
|
|
110
|
-
state.order,
|
|
111
|
-
serverOrderObjectsInOrder.map((e) => String(e.id))
|
|
112
|
-
);
|
|
113
|
-
assignReactiveObject(state.objectsInOrder, serverOrderObjectsInOrder);
|
|
108
|
+
assignReactiveObject(state.order, Object.keys(parentState.objects));
|
|
109
|
+
assignReactiveObject(state.objectsInOrder, Object.values(parentState.objects));
|
|
114
110
|
return;
|
|
115
111
|
}
|
|
116
112
|
|
|
117
113
|
let idList = Object.keys(parentState.objects);
|
|
118
|
-
|
|
119
114
|
idList.sort((xKey, yKey) => {
|
|
120
115
|
const xCriteria = state.sortCriteria[xKey];
|
|
121
116
|
const yCriteria = state.sortCriteria[yKey];
|
|
@@ -166,7 +161,8 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
|
|
|
166
161
|
immediate: true,
|
|
167
162
|
});
|
|
168
163
|
|
|
169
|
-
|
|
164
|
+
// watching parentState.order triggers some out of order `computed`s, now that listInstance.order is a computed.
|
|
165
|
+
watch([toRef(state, "orderByDesc"), () => state.sortCriteria], throttledSortWatch, {
|
|
170
166
|
deep: true,
|
|
171
167
|
});
|
|
172
168
|
onScopeDispose(() => {
|
package/use/listSubscription.js
CHANGED
|
@@ -4,7 +4,7 @@ import { cloneDeep, isEmpty, isObject } from "lodash";
|
|
|
4
4
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
5
5
|
import inspect from "browser-util-inspect";
|
|
6
6
|
import { useCancellableIntent } from "../utils/cancellableIntent";
|
|
7
|
-
import loadingCombine from "../utils/loadingCombine";
|
|
7
|
+
import { loadingCombine } from "../utils/loadingCombine";
|
|
8
8
|
|
|
9
9
|
export class ListSubscriptionError extends Error {
|
|
10
10
|
constructor(message) {
|
|
@@ -3,7 +3,7 @@ import { computed, effectScope, reactive, toRef } from "vue";
|
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
4
|
import { useObjectInstance } from "./objectInstance";
|
|
5
5
|
import { useCancellableIntent } from "../utils/cancellableIntent";
|
|
6
|
-
import loadingCombine from "../utils/loadingCombine";
|
|
6
|
+
import { loadingCombine } from "../utils/loadingCombine";
|
|
7
7
|
|
|
8
8
|
export class ObjectSubscriptionError extends Error {
|
|
9
9
|
constructor(message) {
|
package/utils/getFakeId.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { isArray, isSet } from "lodash";
|
|
1
|
+
import { isArray, isSet, isMap } from "lodash";
|
|
2
2
|
|
|
3
|
-
export function getFakeId(
|
|
3
|
+
export function getFakeId(arraySetMapOrObject, key = "id") {
|
|
4
4
|
// sets are assumed to be of ids
|
|
5
5
|
// arrays are assumed to be objects with an property matching the passed key
|
|
6
6
|
// objects are assumed to have keys that are ids
|
|
7
7
|
let fakeId;
|
|
8
8
|
let test;
|
|
9
|
-
if (isSet(
|
|
10
|
-
test = () =>
|
|
11
|
-
} else if (isArray(
|
|
12
|
-
test = () =>
|
|
9
|
+
if (isSet(arraySetMapOrObject) || isMap(arraySetMapOrObject)) {
|
|
10
|
+
test = () => arraySetMapOrObject.has(fakeId);
|
|
11
|
+
} else if (isArray(arraySetMapOrObject)) {
|
|
12
|
+
test = () => arraySetMapOrObject.some((item) => item?.[key] === fakeId);
|
|
13
13
|
} else {
|
|
14
|
-
test = () => fakeId in
|
|
14
|
+
test = () => fakeId in arraySetMapOrObject;
|
|
15
15
|
}
|
|
16
16
|
do {
|
|
17
17
|
fakeId = Math.floor(Math.random() * Number.MIN_SAFE_INTEGER);
|
package/utils/index.js
CHANGED
package/utils/loadingCombine.js
CHANGED