@webitel/ui-datalist 26.8.3 → 26.8.5
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/src/index.ts +2 -1
- package/src/modules/card/composables/__tests__/useCardRouting.spec.ts +146 -0
- package/src/modules/card/composables/useCardComponent.ts +9 -0
- package/src/modules/card/composables/useCardRouting.ts +12 -6
- package/src/modules/card/composables/useCardStoreProvider.ts +26 -0
- package/src/modules/card/composables/useNestedCardComponent.ts +8 -5
- package/src/modules/card/index.ts +1 -0
- package/src/modules/card/stores/__tests__/createCardStore.spec.ts +73 -0
- package/src/modules/card/stores/createCardStore.ts +23 -2
- package/src/modules/card/types/CardStore.types.ts +8 -0
- package/src/modules/table/__tests__/createTableStore.spec.ts +262 -0
- package/src/modules/table/__tests__/useNestedTableList.spec.ts +218 -0
- package/src/modules/table/composables/useNestedTableList.ts +57 -0
- package/src/modules/table/createTableStore.store.ts +37 -0
- package/types/.tsbuildinfo +1 -1
- package/types/index.d.ts +2 -1
- package/types/modules/card/composables/useCardRouting.d.ts +10 -6
- package/types/modules/card/composables/useCardStoreProvider.d.ts +18 -0
- package/types/modules/card/composables/useNestedCardComponent.d.ts +6 -2
- package/types/modules/card/index.d.ts +1 -0
- package/types/modules/card/stores/createCardStore.d.ts +19 -10
- package/types/modules/card/types/CardStore.types.d.ts +7 -0
- package/types/modules/filter-presets/stores/createFilterPresetsStore.d.ts +2 -0
- package/types/modules/filters/modules/filterConfig/components/case-service/config.d.ts +1 -1
- package/types/modules/permissions-page/stores/createPermissionsStore.d.ts +2 -0
- package/types/modules/table/composables/useNestedTableList.d.ts +27 -0
- package/types/modules/table/createTableStore.store.d.ts +4 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { createPinia, setActivePinia } from 'pinia';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { defineComponent, h } from 'vue';
|
|
5
|
+
import { createMemoryHistory, createRouter, type Router } from 'vue-router';
|
|
6
|
+
|
|
7
|
+
import type { DatalistTableHeader } from '../../types/tableStore.types';
|
|
8
|
+
import { createTableStore } from '../createTableStore.store';
|
|
9
|
+
|
|
10
|
+
const routes = [
|
|
11
|
+
{
|
|
12
|
+
path: '/cases',
|
|
13
|
+
name: 'cases',
|
|
14
|
+
component: {
|
|
15
|
+
template: '<div />',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const headers = [
|
|
21
|
+
{
|
|
22
|
+
value: 'name',
|
|
23
|
+
field: 'name',
|
|
24
|
+
show: true,
|
|
25
|
+
sort: null,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: 'subject',
|
|
29
|
+
field: 'subject',
|
|
30
|
+
show: true,
|
|
31
|
+
sort: null,
|
|
32
|
+
},
|
|
33
|
+
] as DatalistTableHeader[];
|
|
34
|
+
|
|
35
|
+
const flush = async () => {
|
|
36
|
+
for (let i = 0; i < 5; i += 1) {
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
`setupStore` runs once per store, and the watchers it depends on must outlive
|
|
43
|
+
every component that renders the table. Registering them from the caller left
|
|
44
|
+
them owned by that component's effect scope, so a card tab that had been
|
|
45
|
+
walked away from came back inert — it loaded once and then ignored sort,
|
|
46
|
+
pagination, page size, column visibility and filters
|
|
47
|
+
([WTEL-10308](https://webitel.atlassian.net/browse/WTEL-10308), fixed by
|
|
48
|
+
[WTEL-10277](https://webitel.atlassian.net/browse/WTEL-10277)).
|
|
49
|
+
|
|
50
|
+
Tables that keep persistence were spared by accident back then: their setup
|
|
51
|
+
awaits the restore, so the watchers landed with no active scope. Both settings
|
|
52
|
+
are covered here, because that difference was invisible until it broke.
|
|
53
|
+
*/
|
|
54
|
+
describe('tableStoreBody', () => {
|
|
55
|
+
let router: Router;
|
|
56
|
+
let getList: ReturnType<typeof vi.fn>;
|
|
57
|
+
|
|
58
|
+
beforeEach(async () => {
|
|
59
|
+
localStorage.clear();
|
|
60
|
+
sessionStorage.clear();
|
|
61
|
+
setActivePinia(createPinia());
|
|
62
|
+
|
|
63
|
+
router = createRouter({
|
|
64
|
+
history: createMemoryHistory(),
|
|
65
|
+
routes,
|
|
66
|
+
});
|
|
67
|
+
await router.push('/cases');
|
|
68
|
+
await router.isReady();
|
|
69
|
+
|
|
70
|
+
getList = vi.fn().mockResolvedValue({
|
|
71
|
+
items: [],
|
|
72
|
+
next: false,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
a tab on a card page: the table component is unmounted and mounted back as
|
|
78
|
+
the user walks the tabs, and the store outlives all of it
|
|
79
|
+
*/
|
|
80
|
+
const mountTab = (useStore: ReturnType<typeof createTableStore>) => {
|
|
81
|
+
const Tab = defineComponent({
|
|
82
|
+
setup() {
|
|
83
|
+
const store = useStore();
|
|
84
|
+
/* a nested list is initialized with the card's id */
|
|
85
|
+
store.initialize({
|
|
86
|
+
parentId: '42',
|
|
87
|
+
});
|
|
88
|
+
return () => h('div');
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return mount(Tab, {
|
|
93
|
+
global: {
|
|
94
|
+
plugins: [
|
|
95
|
+
router,
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
describe.each([
|
|
102
|
+
[
|
|
103
|
+
'with persistence',
|
|
104
|
+
false,
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
'without persistence',
|
|
108
|
+
true,
|
|
109
|
+
],
|
|
110
|
+
])('%s', (_label, disablePersistence) => {
|
|
111
|
+
let useStore: ReturnType<typeof createTableStore>;
|
|
112
|
+
|
|
113
|
+
beforeEach(async () => {
|
|
114
|
+
useStore = createTableStore(`cases-${disablePersistence}/datalist`, {
|
|
115
|
+
apiModule: {
|
|
116
|
+
getList,
|
|
117
|
+
},
|
|
118
|
+
headers,
|
|
119
|
+
disablePersistence,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const first = mountTab(useStore);
|
|
123
|
+
await flush();
|
|
124
|
+
first.unmount();
|
|
125
|
+
await flush();
|
|
126
|
+
|
|
127
|
+
mountTab(useStore);
|
|
128
|
+
await flush();
|
|
129
|
+
|
|
130
|
+
getList.mockClear();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('reloads on sort after the component is remounted', async () => {
|
|
134
|
+
const store = useStore();
|
|
135
|
+
|
|
136
|
+
store.updateSort(store.headers[0]);
|
|
137
|
+
await flush();
|
|
138
|
+
|
|
139
|
+
expect(getList).toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('reloads on size change after the component is remounted', async () => {
|
|
143
|
+
const store = useStore();
|
|
144
|
+
|
|
145
|
+
store.updateSize(20);
|
|
146
|
+
await flush();
|
|
147
|
+
|
|
148
|
+
expect(getList).toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('reloads on page change after the component is remounted', async () => {
|
|
152
|
+
const store = useStore();
|
|
153
|
+
|
|
154
|
+
store.updatePage(2);
|
|
155
|
+
await flush();
|
|
156
|
+
|
|
157
|
+
expect(getList).toHaveBeenCalled();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('reloads on search after the component is remounted', async () => {
|
|
161
|
+
const store = useStore();
|
|
162
|
+
|
|
163
|
+
store.addFilter({
|
|
164
|
+
name: 'search',
|
|
165
|
+
value: 'lorem',
|
|
166
|
+
});
|
|
167
|
+
await flush();
|
|
168
|
+
|
|
169
|
+
expect(getList).toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('reloads on a hidden column after the component is remounted', async () => {
|
|
173
|
+
const store = useStore();
|
|
174
|
+
|
|
175
|
+
store.updateShownHeaders(
|
|
176
|
+
store.headers.map((header, index) => ({
|
|
177
|
+
...header,
|
|
178
|
+
show: index === 0,
|
|
179
|
+
})),
|
|
180
|
+
);
|
|
181
|
+
await flush();
|
|
182
|
+
|
|
183
|
+
expect(getList).toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
/*
|
|
187
|
+
A nested list is emptied by the card store it is registered with, and until
|
|
188
|
+
it has a parent again it must not load: the store keeps the parent it was
|
|
189
|
+
initialized with, so a reload in between queried the previous card's record.
|
|
190
|
+
|
|
191
|
+
[WTEL-10350](https://webitel.atlassian.net/browse/WTEL-10350)
|
|
192
|
+
*/
|
|
193
|
+
describe('$reset of a nested list', () => {
|
|
194
|
+
let useStore: ReturnType<typeof createTableStore>;
|
|
195
|
+
|
|
196
|
+
beforeEach(async () => {
|
|
197
|
+
useStore = createTableStore('cases-reset/datalist', {
|
|
198
|
+
apiModule: {
|
|
199
|
+
getList,
|
|
200
|
+
},
|
|
201
|
+
headers,
|
|
202
|
+
disablePersistence: true,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
mountTab(useStore);
|
|
206
|
+
await flush();
|
|
207
|
+
getList.mockClear();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('drops the rows, the parent and the pagination', async () => {
|
|
211
|
+
const store = useStore();
|
|
212
|
+
store.updatePage(3);
|
|
213
|
+
await flush();
|
|
214
|
+
|
|
215
|
+
store.$reset();
|
|
216
|
+
|
|
217
|
+
expect(store.dataList).toEqual([]);
|
|
218
|
+
expect(store.selected).toEqual([]);
|
|
219
|
+
expect(store.error).toBeNull();
|
|
220
|
+
expect(store.page).toBe(1);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('keeps the headers, which are the user column choice', async () => {
|
|
224
|
+
const store = useStore();
|
|
225
|
+
store.updateShownHeaders(
|
|
226
|
+
store.headers.map((header, index) => ({
|
|
227
|
+
...header,
|
|
228
|
+
show: index === 0,
|
|
229
|
+
})),
|
|
230
|
+
);
|
|
231
|
+
await flush();
|
|
232
|
+
const shown = store.headers.map((header) => header.show);
|
|
233
|
+
|
|
234
|
+
store.$reset();
|
|
235
|
+
|
|
236
|
+
expect(store.headers.map((header) => header.show)).toEqual(shown);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('loads nothing until it has a parent again', async () => {
|
|
240
|
+
const store = useStore();
|
|
241
|
+
store.$reset();
|
|
242
|
+
getList.mockClear();
|
|
243
|
+
|
|
244
|
+
await store.loadDataList();
|
|
245
|
+
store.updatePage(2);
|
|
246
|
+
await flush();
|
|
247
|
+
|
|
248
|
+
expect(getList).not.toHaveBeenCalled();
|
|
249
|
+
|
|
250
|
+
store.initialize({
|
|
251
|
+
parentId: '43',
|
|
252
|
+
});
|
|
253
|
+
await flush();
|
|
254
|
+
|
|
255
|
+
expect(getList).toHaveBeenCalledWith(
|
|
256
|
+
expect.objectContaining({
|
|
257
|
+
parentId: '43',
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { createPinia, setActivePinia } from 'pinia';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { defineComponent, h, ref } from 'vue';
|
|
5
|
+
import { createMemoryHistory, createRouter, type Router } from 'vue-router';
|
|
6
|
+
|
|
7
|
+
import { provideCardStore } from '../../card/composables/useCardStoreProvider';
|
|
8
|
+
import type { CardItemId } from '../../card/types/CardStore.types';
|
|
9
|
+
import type { DatalistTableHeader } from '../../types/tableStore.types';
|
|
10
|
+
import { useNestedTableList } from '../composables/useNestedTableList';
|
|
11
|
+
import { createTableStore } from '../createTableStore.store';
|
|
12
|
+
|
|
13
|
+
const routes = [
|
|
14
|
+
{
|
|
15
|
+
path: '/queues',
|
|
16
|
+
name: 'queues',
|
|
17
|
+
component: {
|
|
18
|
+
template: '<div />',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const headers = [
|
|
24
|
+
{
|
|
25
|
+
value: 'name',
|
|
26
|
+
field: 'name',
|
|
27
|
+
show: true,
|
|
28
|
+
sort: null,
|
|
29
|
+
},
|
|
30
|
+
] as DatalistTableHeader[];
|
|
31
|
+
|
|
32
|
+
const flush = async () => {
|
|
33
|
+
for (let i = 0; i < 5; i += 1) {
|
|
34
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
A nested list store is shared by every card of its kind and outlives all of
|
|
40
|
+
them, so a tab that read it directly showed the rows of whichever record was
|
|
41
|
+
opened before — and a card for an unsaved record showed them until saved.
|
|
42
|
+
|
|
43
|
+
[WTEL-10350](https://webitel.atlassian.net/browse/WTEL-10350)
|
|
44
|
+
*/
|
|
45
|
+
describe('useNestedTableList', () => {
|
|
46
|
+
let router: Router;
|
|
47
|
+
let getList: ReturnType<typeof vi.fn>;
|
|
48
|
+
let useStore: ReturnType<typeof createTableStore>;
|
|
49
|
+
let namespace = 0;
|
|
50
|
+
|
|
51
|
+
beforeEach(async () => {
|
|
52
|
+
localStorage.clear();
|
|
53
|
+
sessionStorage.clear();
|
|
54
|
+
setActivePinia(createPinia());
|
|
55
|
+
|
|
56
|
+
router = createRouter({
|
|
57
|
+
history: createMemoryHistory(),
|
|
58
|
+
routes,
|
|
59
|
+
});
|
|
60
|
+
await router.push('/queues');
|
|
61
|
+
await router.isReady();
|
|
62
|
+
|
|
63
|
+
getList = vi.fn().mockImplementation(({ parentId }) =>
|
|
64
|
+
Promise.resolve({
|
|
65
|
+
items: [
|
|
66
|
+
{
|
|
67
|
+
id: 1,
|
|
68
|
+
name: `row of ${parentId}`,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
next: false,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
namespace += 1;
|
|
76
|
+
useStore = createTableStore(`queue-buckets-${namespace}/datalist`, {
|
|
77
|
+
apiModule: {
|
|
78
|
+
getList,
|
|
79
|
+
},
|
|
80
|
+
headers,
|
|
81
|
+
disablePersistence: true,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/** a card page with one nested tab in it */
|
|
86
|
+
const openCard = (itemId: CardItemId) => {
|
|
87
|
+
const cardItemId = ref<CardItemId>(itemId);
|
|
88
|
+
const nestedLists = new Set<{
|
|
89
|
+
$reset: () => void;
|
|
90
|
+
}>();
|
|
91
|
+
|
|
92
|
+
const Tab = defineComponent({
|
|
93
|
+
setup() {
|
|
94
|
+
useNestedTableList({
|
|
95
|
+
useTableStore: useStore,
|
|
96
|
+
});
|
|
97
|
+
return () => h('div');
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const Card = defineComponent({
|
|
102
|
+
setup() {
|
|
103
|
+
provideCardStore({
|
|
104
|
+
get itemId() {
|
|
105
|
+
return cardItemId.value;
|
|
106
|
+
},
|
|
107
|
+
registerNestedList: (list) => nestedLists.add(list),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return () => h(Tab);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const card = mount(Card, {
|
|
115
|
+
global: {
|
|
116
|
+
plugins: [
|
|
117
|
+
router,
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/** what the card page does on unmount */
|
|
123
|
+
const close = () => {
|
|
124
|
+
for (const list of nestedLists) list.$reset();
|
|
125
|
+
nestedLists.clear();
|
|
126
|
+
card.unmount();
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
card,
|
|
131
|
+
close,
|
|
132
|
+
cardItemId,
|
|
133
|
+
nestedLists,
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
it('loads the list of the card it is opened in', async () => {
|
|
138
|
+
openCard('42');
|
|
139
|
+
await flush();
|
|
140
|
+
|
|
141
|
+
expect(getList).toHaveBeenCalledWith(
|
|
142
|
+
expect.objectContaining({
|
|
143
|
+
parentId: '42',
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
expect(useStore().dataList).toEqual([
|
|
147
|
+
{
|
|
148
|
+
id: 1,
|
|
149
|
+
name: 'row of 42',
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('registers itself with the card, which empties it on close', async () => {
|
|
155
|
+
const first = openCard('42');
|
|
156
|
+
await flush();
|
|
157
|
+
expect(first.nestedLists.size).toBe(1);
|
|
158
|
+
|
|
159
|
+
first.close();
|
|
160
|
+
await flush();
|
|
161
|
+
|
|
162
|
+
expect(useStore().dataList).toEqual([]);
|
|
163
|
+
expect(first.nestedLists.size).toBe(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('shows no rows of the previously opened card', async () => {
|
|
167
|
+
const first = openCard('42');
|
|
168
|
+
await flush();
|
|
169
|
+
first.close();
|
|
170
|
+
await flush();
|
|
171
|
+
|
|
172
|
+
getList.mockClear();
|
|
173
|
+
const second = openCard(null); // a record that is not saved yet
|
|
174
|
+
await flush();
|
|
175
|
+
|
|
176
|
+
expect(getList).not.toHaveBeenCalled();
|
|
177
|
+
expect(useStore().dataList).toEqual([]);
|
|
178
|
+
second.close();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('loads once the unsaved record gets its id', async () => {
|
|
182
|
+
const card = openCard(null);
|
|
183
|
+
await flush();
|
|
184
|
+
expect(getList).not.toHaveBeenCalled();
|
|
185
|
+
|
|
186
|
+
card.cardItemId.value = '43';
|
|
187
|
+
await flush();
|
|
188
|
+
|
|
189
|
+
expect(getList).toHaveBeenCalledWith(
|
|
190
|
+
expect.objectContaining({
|
|
191
|
+
parentId: '43',
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
expect(useStore().dataList).toEqual([
|
|
195
|
+
{
|
|
196
|
+
id: 1,
|
|
197
|
+
name: 'row of 43',
|
|
198
|
+
},
|
|
199
|
+
]);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('does not reload the previous parent when the store reloads itself', async () => {
|
|
203
|
+
const first = openCard('42');
|
|
204
|
+
await flush();
|
|
205
|
+
first.close();
|
|
206
|
+
await flush();
|
|
207
|
+
|
|
208
|
+
getList.mockClear();
|
|
209
|
+
const store = useStore();
|
|
210
|
+
|
|
211
|
+
// what a pagination change does, with no card to belong to
|
|
212
|
+
store.updatePage(2);
|
|
213
|
+
await flush();
|
|
214
|
+
|
|
215
|
+
expect(getList).not.toHaveBeenCalled();
|
|
216
|
+
expect(store.dataList).toEqual([]);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { StoreDefinition } from 'pinia';
|
|
2
|
+
import { computed, type MaybeRefOrGetter, toValue, watch } from 'vue';
|
|
3
|
+
|
|
4
|
+
import { useInjectedCardStore } from '../../card/composables/useCardStoreProvider';
|
|
5
|
+
import type { CardItemId } from '../../card/types/CardStore.types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The list of a card page's nested tab.
|
|
9
|
+
*
|
|
10
|
+
* The store is shared by every card of its kind and outlives all of them, so a
|
|
11
|
+
* tab that read it directly showed the rows of whichever record was opened
|
|
12
|
+
* before — and a card for an unsaved record, having no id to initialize with,
|
|
13
|
+
* showed them until it was saved.
|
|
14
|
+
*
|
|
15
|
+
* Registering the list with the card store makes the card responsible for
|
|
16
|
+
* emptying it, and the parent to load comes from the card's own `itemId`: it
|
|
17
|
+
* arrives late for a record created from a nested tab, and the list follows.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* const tableStore = useNestedTableList({ useTableStore: useQueueBucketsStore });
|
|
21
|
+
* const { dataList, isLoading } = storeToRefs(tableStore);
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* [WTEL-10350](https://webitel.atlassian.net/browse/WTEL-10350)
|
|
25
|
+
*/
|
|
26
|
+
export const useNestedTableList = ({
|
|
27
|
+
useTableStore,
|
|
28
|
+
parentId,
|
|
29
|
+
}: {
|
|
30
|
+
useTableStore: StoreDefinition;
|
|
31
|
+
/** only where the parent is not the card's own record */
|
|
32
|
+
parentId?: MaybeRefOrGetter<CardItemId>;
|
|
33
|
+
}) => {
|
|
34
|
+
const tableStore = useTableStore();
|
|
35
|
+
const cardStore = useInjectedCardStore();
|
|
36
|
+
|
|
37
|
+
cardStore?.registerNestedList(tableStore);
|
|
38
|
+
|
|
39
|
+
const parent = computed<CardItemId>(
|
|
40
|
+
() => toValue(parentId) ?? cardStore?.itemId ?? null,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
watch(
|
|
44
|
+
parent,
|
|
45
|
+
(id) => {
|
|
46
|
+
if (id)
|
|
47
|
+
tableStore.initialize({
|
|
48
|
+
parentId: id,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
immediate: true,
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return tableStore;
|
|
57
|
+
};
|
|
@@ -34,6 +34,13 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
34
34
|
const useFiltersStore = createTableFiltersStore(namespace, config);
|
|
35
35
|
|
|
36
36
|
const parentId = ref();
|
|
37
|
+
/**
|
|
38
|
+
* A list initialized with a `parentId` is nested in a card page, and stays
|
|
39
|
+
* nested for the store's lifetime — the api module addresses its parent.
|
|
40
|
+
* `parentId` is dropped on `$reset`, this is not: it is what tells a reset
|
|
41
|
+
* store apart from a registry one that never had a parent.
|
|
42
|
+
*/
|
|
43
|
+
const isNested = ref(false);
|
|
37
44
|
|
|
38
45
|
const paginationStore = usePaginationStore();
|
|
39
46
|
const { page, size, next } = makeThisToRefs<typeof paginationStore>(
|
|
@@ -129,6 +136,15 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
129
136
|
const loadDataList = async ({
|
|
130
137
|
withLoading = true,
|
|
131
138
|
}: LoadDataListOptions = {}) => {
|
|
139
|
+
/*
|
|
140
|
+
a nested list with no parent has nothing to address: either its card was
|
|
141
|
+
left (the card store reset it) or the record is not saved yet. Loading
|
|
142
|
+
here would query whatever parent the store held before.
|
|
143
|
+
|
|
144
|
+
[WTEL-10350](https://webitel.atlassian.net/browse/WTEL-10350)
|
|
145
|
+
*/
|
|
146
|
+
if (isNested.value && !parentId.value) return;
|
|
147
|
+
|
|
132
148
|
if (withLoading) {
|
|
133
149
|
isLoading.value = true;
|
|
134
150
|
}
|
|
@@ -344,6 +360,7 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
344
360
|
} = {}) => {
|
|
345
361
|
if (storeParentId) {
|
|
346
362
|
parentId.value = storeParentId;
|
|
363
|
+
isNested.value = true;
|
|
347
364
|
}
|
|
348
365
|
|
|
349
366
|
const isStoreAlreadySetUp = isStoreSetUp.value;
|
|
@@ -365,6 +382,24 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
365
382
|
return loadDataList();
|
|
366
383
|
};
|
|
367
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Drops everything that belonged to one parent record. Called by the card
|
|
387
|
+
* store this list is registered with, when the card page goes away.
|
|
388
|
+
*
|
|
389
|
+
* Headers are left alone — they are the user's column choice, restored once
|
|
390
|
+
* per app lifetime — and so is `isStoreSetUp`, for the same reason.
|
|
391
|
+
*/
|
|
392
|
+
const $reset = () => {
|
|
393
|
+
dataList.value = [];
|
|
394
|
+
selected.value = [];
|
|
395
|
+
error.value = null;
|
|
396
|
+
isLoading.value = false;
|
|
397
|
+
parentId.value = undefined;
|
|
398
|
+
|
|
399
|
+
paginationStore.$reset();
|
|
400
|
+
filtersManager.value.reset();
|
|
401
|
+
};
|
|
402
|
+
|
|
368
403
|
const resetInfiniteScrollTableParamsToDefaults = () => {
|
|
369
404
|
paginationStore.$reset();
|
|
370
405
|
filtersManager.value.reset();
|
|
@@ -373,6 +408,7 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
373
408
|
|
|
374
409
|
return {
|
|
375
410
|
isStoreSetUp, // internal export for pinia devtools
|
|
411
|
+
isNested, // internal export for pinia devtools
|
|
376
412
|
|
|
377
413
|
dataList,
|
|
378
414
|
selected,
|
|
@@ -395,6 +431,7 @@ export const tableStoreBody = <Entity extends Identifiable>(
|
|
|
395
431
|
|
|
396
432
|
setupStore, // only setup, no data loading
|
|
397
433
|
initialize, // setup + load data
|
|
434
|
+
$reset, // drop the data and the parent binding
|
|
398
435
|
syncPersistence, // republish store state into the route query
|
|
399
436
|
|
|
400
437
|
loadDataList,
|