@webitel/ui-datalist 1.1.67 → 1.1.68
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/modules/headers/__tests__/createTableHeadersStore.spec.ts +144 -1
- package/src/modules/headers/createTableHeadersStore.ts +21 -4
- package/src/modules/types/tableStore.types.ts +7 -0
- package/types/.tsbuildinfo +1 -1
- package/types/modules/filter-presets/stores/createFilterPresetsStore.d.ts +3 -0
- package/types/modules/headers/createTableHeadersStore.d.ts +6 -0
- package/types/modules/permissions-page/stores/createPermissionsStore.d.ts +6 -0
- package/types/modules/table/createTableStore.store.d.ts +6 -0
- package/types/modules/types/tableStore.types.d.ts +7 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { createApp } from 'vue';
|
|
2
|
+
import { createApp, ref } from 'vue';
|
|
3
3
|
import { createMemoryHistory, createRouter, type Router } from 'vue-router';
|
|
4
4
|
|
|
5
5
|
import type { DatalistTableHeader } from '../../types/tableStore.types';
|
|
@@ -141,6 +141,149 @@ describe('tableHeadersStoreBody', () => {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
describe('access', () => {
|
|
145
|
+
const createGatedStore = (gated: DatalistTableHeader[]) =>
|
|
146
|
+
tableHeadersStoreBody({
|
|
147
|
+
rawHeaders: [
|
|
148
|
+
...rawHeaders.map((header) => ({
|
|
149
|
+
...header,
|
|
150
|
+
})),
|
|
151
|
+
...gated,
|
|
152
|
+
],
|
|
153
|
+
id,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('drops a denied header from headers, shownHeaders and fields', () => {
|
|
157
|
+
const store = createGatedStore([
|
|
158
|
+
{
|
|
159
|
+
field: 'agent',
|
|
160
|
+
show: true,
|
|
161
|
+
sort: null,
|
|
162
|
+
access: () => false,
|
|
163
|
+
} as DatalistTableHeader,
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
expect(store.headers.value.map(({ field }) => field)).not.toContain(
|
|
167
|
+
'agent',
|
|
168
|
+
);
|
|
169
|
+
expect(store.shownHeaders.value.map(({ field }) => field)).not.toContain(
|
|
170
|
+
'agent',
|
|
171
|
+
);
|
|
172
|
+
expect(store.fields.value).not.toContain('agent');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('keeps a granted header and leaves ungated headers untouched', () => {
|
|
176
|
+
const store = createGatedStore([
|
|
177
|
+
{
|
|
178
|
+
field: 'agent',
|
|
179
|
+
show: true,
|
|
180
|
+
sort: null,
|
|
181
|
+
access: () => true,
|
|
182
|
+
} as DatalistTableHeader,
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
expect(store.fields.value).toEqual([
|
|
186
|
+
'name',
|
|
187
|
+
'subject',
|
|
188
|
+
'agent',
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('unwraps a ref returned by the access gate', () => {
|
|
193
|
+
const store = createGatedStore([
|
|
194
|
+
{
|
|
195
|
+
field: 'agent',
|
|
196
|
+
show: true,
|
|
197
|
+
sort: null,
|
|
198
|
+
access: () => ref(false),
|
|
199
|
+
} as DatalistTableHeader,
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
expect(store.fields.value).toEqual([
|
|
203
|
+
'name',
|
|
204
|
+
'subject',
|
|
205
|
+
]);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('does not resurrect a denied header from the persisted fields', async () => {
|
|
209
|
+
await router.push({
|
|
210
|
+
name: 'cases',
|
|
211
|
+
query: {
|
|
212
|
+
fields: 'agent,createdAt',
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const store = createGatedStore([
|
|
217
|
+
{
|
|
218
|
+
field: 'agent',
|
|
219
|
+
show: true,
|
|
220
|
+
sort: null,
|
|
221
|
+
access: () => false,
|
|
222
|
+
} as DatalistTableHeader,
|
|
223
|
+
]);
|
|
224
|
+
await runWithRouter(() => store.setupPersistence());
|
|
225
|
+
|
|
226
|
+
expect(store.fields.value).toEqual([
|
|
227
|
+
'createdAt',
|
|
228
|
+
]);
|
|
229
|
+
expect(store.headers.value.map(({ field }) => field)).not.toContain(
|
|
230
|
+
'agent',
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('does not resurrect a denied header on reset', () => {
|
|
235
|
+
const store = createGatedStore([
|
|
236
|
+
{
|
|
237
|
+
field: 'agent',
|
|
238
|
+
show: true,
|
|
239
|
+
sort: null,
|
|
240
|
+
access: () => false,
|
|
241
|
+
} as DatalistTableHeader,
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
store.$reset();
|
|
245
|
+
|
|
246
|
+
expect(store.headers.value.map(({ field }) => field)).not.toContain(
|
|
247
|
+
'agent',
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('keeps a field alive while another allowed header still claims it', async () => {
|
|
252
|
+
await router.push({
|
|
253
|
+
name: 'cases',
|
|
254
|
+
query: {
|
|
255
|
+
fields: 'member',
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
/* `member` and `memberId` share one API field — only one is gated */
|
|
260
|
+
const store = createGatedStore([
|
|
261
|
+
{
|
|
262
|
+
value: 'member',
|
|
263
|
+
field: 'member',
|
|
264
|
+
show: true,
|
|
265
|
+
sort: null,
|
|
266
|
+
} as DatalistTableHeader,
|
|
267
|
+
{
|
|
268
|
+
value: 'memberId',
|
|
269
|
+
field: 'member',
|
|
270
|
+
show: false,
|
|
271
|
+
sort: null,
|
|
272
|
+
access: () => false,
|
|
273
|
+
} as DatalistTableHeader,
|
|
274
|
+
]);
|
|
275
|
+
await runWithRouter(() => store.setupPersistence());
|
|
276
|
+
|
|
277
|
+
expect(store.headers.value.map(({ value }) => value)).toContain('member');
|
|
278
|
+
expect(store.headers.value.map(({ value }) => value)).not.toContain(
|
|
279
|
+
'memberId',
|
|
280
|
+
);
|
|
281
|
+
expect(store.fields.value).toEqual([
|
|
282
|
+
'member',
|
|
283
|
+
]);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
144
287
|
describe('syncPersistence', () => {
|
|
145
288
|
it('publishes a customized column set', async () => {
|
|
146
289
|
const store = await setUpStore();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WtTableSortOrder } from '@webitel/ui-sdk/components/wt-table/types/WtTable';
|
|
2
2
|
import { sortToQueryAdapter } from '@webitel/ui-sdk/scripts';
|
|
3
3
|
import { SortSymbols } from '@webitel/ui-sdk/scripts/sortQueryAdapters';
|
|
4
|
-
import { computed, nextTick, ref } from 'vue';
|
|
4
|
+
import { computed, nextTick, ref, unref } from 'vue';
|
|
5
5
|
|
|
6
6
|
import { createDatalistStore } from '../_shared/createDatalistStore';
|
|
7
7
|
import {
|
|
@@ -24,7 +24,21 @@ export const tableHeadersStoreBody = ({
|
|
|
24
24
|
rawHeaders,
|
|
25
25
|
id,
|
|
26
26
|
}: TableHeadersStoreBodyParams) => {
|
|
27
|
-
const
|
|
27
|
+
const isAllowed = (header: DatalistTableHeader) =>
|
|
28
|
+
!header.access || !!unref(header.access());
|
|
29
|
+
|
|
30
|
+
const allowedHeaders = rawHeaders.filter(isAllowed);
|
|
31
|
+
|
|
32
|
+
/* headers may share a `field`, so it stays allowed while any of them is */
|
|
33
|
+
const allowedFields = new Set(allowedHeaders.map((header) => header.field));
|
|
34
|
+
const deniedFields = new Set(
|
|
35
|
+
rawHeaders
|
|
36
|
+
.filter((header) => !isAllowed(header))
|
|
37
|
+
.map((header) => header.field)
|
|
38
|
+
.filter((field) => !allowedFields.has(field)),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const headers = ref<DatalistTableHeader[]>(allowedHeaders);
|
|
28
42
|
const isReorderingColumn = ref(false);
|
|
29
43
|
|
|
30
44
|
const shownHeaders = computed(() => {
|
|
@@ -64,7 +78,7 @@ export const tableHeadersStoreBody = ({
|
|
|
64
78
|
});
|
|
65
79
|
|
|
66
80
|
const $reset = () => {
|
|
67
|
-
headers.value =
|
|
81
|
+
headers.value = allowedHeaders;
|
|
68
82
|
};
|
|
69
83
|
|
|
70
84
|
const updateShownHeaders = (value: DatalistTableHeader[]) => {
|
|
@@ -112,7 +126,10 @@ export const tableHeadersStoreBody = ({
|
|
|
112
126
|
});
|
|
113
127
|
};
|
|
114
128
|
|
|
115
|
-
const updateFields = (
|
|
129
|
+
const updateFields = (persistedFields: string[]) => {
|
|
130
|
+
/* persisted state predates the gate, and unknown fields are revived below */
|
|
131
|
+
const fields = persistedFields.filter((field) => !deniedFields.has(field));
|
|
132
|
+
|
|
116
133
|
const fieldsSet = new Set(fields);
|
|
117
134
|
const mainFieldNames = new Set(headers.value.map((header) => header.field));
|
|
118
135
|
|
|
@@ -13,6 +13,13 @@ import type { DatalistStoreProviderType } from './StoreProvider';
|
|
|
13
13
|
export type DatalistTableHeader = WtTableHeader & {
|
|
14
14
|
field: string;
|
|
15
15
|
shouldBeInitialized?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Access gate supplied by the consuming app, evaluated once when the headers
|
|
18
|
+
* store is created. Returning `false` drops the column completely: absent
|
|
19
|
+
* from the column picker, never rendered, never requested, and not
|
|
20
|
+
* restorable from persisted state. Omitted means allowed.
|
|
21
|
+
*/
|
|
22
|
+
access?: () => boolean | Ref<boolean>;
|
|
16
23
|
};
|
|
17
24
|
|
|
18
25
|
export type TrackSelectedRowBy<T> = (row: T) => T;
|