@dile/crud 2.9.5 → 2.10.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/components/steps-form/index.js +3 -0
- package/components/steps-form/src/DileStepForm.js +202 -0
- package/components/steps-form/src/DileStepNextPrev.js +134 -0
- package/components/steps-form/src/DileStepsForm.js +347 -0
- package/components/steps-form/step-form.js +5 -0
- package/components/steps-form/step-next-prev.js +5 -0
- package/components/steps-form/steps-form.js +5 -0
- package/lib/i18n/en.js +6 -1
- package/lib/i18n/es.js +6 -1
- package/package.json +3 -3
- package/components/action/src/DileCrudSingleActions.spec.js +0 -126
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-closing-dialog-with-ESC-and-reopening-without-data-shows-reset-form-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-initialData-is-cleared-after-being-applied-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-initialData-is-only-applied-once-and-cleared-after-opening-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-behavior-matches-original-when-no-initialData-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-opens-confirm-dialog-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-with-initialData-sets-form-values-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-with-initialData-sets-form-values-before-opening-dialog-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-without-initialData-resets-form-1.png +0 -0
- package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-without-initialData-resets-form-to-empty-1.png +0 -0
- package/components/ajax/ajax.spec.js +0 -35
- package/components/ajax-select-crud/ajax-select-crud-overlay.spec.js +0 -186
- package/components/crud/__screenshots__/crud.spec.js/dile-crud-list-grid-view-switch-defaults-to-the-item-template-and-toggles-to-the-grid-on-click-when-both-views-are-configured-1.png +0 -0
- package/components/crud/__screenshots__/crud.spec.js/dile-crud-list-grid-view-switch-does-not-render-the-switch-button-when-customization-disableListWiewSwitch-is-true-1.png +0 -0
- package/components/crud/crud.spec.js +0 -453
- package/components/file-uploader/file-uploader.spec.js +0 -32
- package/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-always-forces-sort-mode-to-external-regardless-of-config-1.png +0 -0
- package/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-forwards-stickyFirstColumn--striped--responsiveMode-and-emptyMessage-with-sensible-defaults-1.png +0 -0
- package/components/list/__screenshots__/crud-data-grid.spec.js/dile-crud-data-grid-leaves-computeRowId-unset-when-config-computeItemId-is-not-a-function-1.png +0 -0
- package/components/list/__screenshots__/crud-list.spec.js/dile-crud-list-scope-changes-and-page-reset-keeps-the-current-page-on-refresh---and-goes-back-to-page-1-on-refresh---resetPage--true----1.png +0 -0
- package/components/list/__screenshots__/crud-list.spec.js/dile-crud-list-scope-changes-and-page-reset-reloads-from-the-first-page-with-the-new-scope-when-belongsTo-and-relationId-change-1.png +0 -0
- package/components/list/__screenshots__/crud-list.spec.js/dile-crud-list-scope-changes-and-page-reset-reloads-when-only-relationId-changes-1.png +0 -0
- package/components/list/crud-data-grid.spec.js +0 -207
- package/components/list/crud-item-actions.spec.js +0 -122
- package/components/list/crud-list.spec.js +0 -455
- package/components/many-relation/many-relation.spec.js +0 -154
- package/components/select-ajax-simple/select-ajax-simple.spec.js +0 -133
- package/components/single/src/DileCrudSingle.spec.js +0 -151
- package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerAction-delegates-to-singleActionsElement-triggerAction-1.png +0 -0
- package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerAction-without-initialData-works-1.png +0 -0
- package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerDirectAction-delegates-to-singleActionsElement-triggerAction-1.png +0 -0
- package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerDirectAction-without-initialData-still-works--backward-compatibility--1.png +0 -0
- package/components/ui/__screenshots__/crud-sort-form.spec.js/dile-crud-sort-form-clicking-the-label-of-an-inactive-option-switches-to-it-using-its-own-configured-direction--without-flipping-it-1.png +0 -0
- package/components/ui/__screenshots__/crud-sort-form.spec.js/dile-crud-sort-form-clicking-the-label-of-the-already-active-option-toggles-its-direction-instead-of-doing-nothing-1.png +0 -0
- package/components/ui/__screenshots__/crud-sort-form.spec.js/dile-crud-sort-form-syncSort-suppresses-exactly-one-radio-group-echo--preserving-the-synced-direction-instead-of-falling-back-to-the-option-static-default-1.png +0 -0
- package/components/ui/crud-sort-form.spec.js +0 -132
- package/lib/CrudConfigBuilder.spec.js +0 -32
- package/lib/deepMerge.spec.js +0 -45
- package/lib/listViewMode.spec.js +0 -73
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { html } from 'lit';
|
|
3
|
-
import { CrudConfigBuilder } from '../../lib/CrudConfigBuilder.js';
|
|
4
|
-
import './crud-list.js';
|
|
5
|
-
import './crud-data-grid.js';
|
|
6
|
-
import '@dile/ui/components/data-grid/data-grid.js';
|
|
7
|
-
|
|
8
|
-
describe('dile-crud-list grid template integration', () => {
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
document.body.innerHTML = '';
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const sampleElements = [
|
|
14
|
-
{ id: 1, name: 'Customer A', email: 'a@example.com' },
|
|
15
|
-
{ id: 2, name: 'Customer B', email: 'b@example.com' },
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
it('renders default dile-crud-list-item when templates.grid is not provided', async () => {
|
|
19
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
20
|
-
templates: {
|
|
21
|
-
item: (item) => html`<span class="item-name">${item.name}</span>`,
|
|
22
|
-
},
|
|
23
|
-
}).getConfig();
|
|
24
|
-
|
|
25
|
-
const el = document.createElement('dile-crud-list');
|
|
26
|
-
el.disableLoadOnStart = true;
|
|
27
|
-
el.config = config;
|
|
28
|
-
el.elements = sampleElements;
|
|
29
|
-
el.loading = false;
|
|
30
|
-
document.body.appendChild(el);
|
|
31
|
-
await el.updateComplete;
|
|
32
|
-
|
|
33
|
-
const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
|
|
34
|
-
expect(listItems.length).toBe(2);
|
|
35
|
-
|
|
36
|
-
const gridContainer = el.shadowRoot.querySelector('.grid-container');
|
|
37
|
-
expect(gridContainer).toBeNull();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('renders generic dile-crud-data-grid when config.grid.columns is defined', async () => {
|
|
41
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
42
|
-
grid: {
|
|
43
|
-
columns: [
|
|
44
|
-
{ field: 'id', header: 'ID' },
|
|
45
|
-
{ field: 'name', header: 'Customer Name' },
|
|
46
|
-
],
|
|
47
|
-
striped: true,
|
|
48
|
-
},
|
|
49
|
-
}).getConfig();
|
|
50
|
-
|
|
51
|
-
const el = document.createElement('dile-crud-list');
|
|
52
|
-
el.disableLoadOnStart = true;
|
|
53
|
-
el.config = config;
|
|
54
|
-
el.elements = sampleElements;
|
|
55
|
-
el.sort = { sortField: 'name', sortDirection: 'desc' };
|
|
56
|
-
el.loading = false;
|
|
57
|
-
document.body.appendChild(el);
|
|
58
|
-
await el.updateComplete;
|
|
59
|
-
|
|
60
|
-
const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
|
|
61
|
-
expect(listItems.length).toBe(0);
|
|
62
|
-
|
|
63
|
-
const crudDataGrid = el.shadowRoot.querySelector('dile-crud-data-grid');
|
|
64
|
-
expect(crudDataGrid).toBeTruthy();
|
|
65
|
-
expect(crudDataGrid.items).toEqual(sampleElements);
|
|
66
|
-
expect(crudDataGrid.columns.length).toBe(3); // id, name + automatically injected Actions
|
|
67
|
-
expect(crudDataGrid.columns[2].header).toBe('Actions');
|
|
68
|
-
expect(crudDataGrid.sort).toEqual({ sortField: 'name', sortDirection: 'desc' });
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('hides actions in traditional list items when disableListActions is true', async () => {
|
|
72
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
73
|
-
customization: {
|
|
74
|
-
disableListActions: true,
|
|
75
|
-
},
|
|
76
|
-
templates: {
|
|
77
|
-
item: (item) => html`<span class="item-name">${item.name}</span>`,
|
|
78
|
-
},
|
|
79
|
-
}).getConfig();
|
|
80
|
-
|
|
81
|
-
const el = document.createElement('dile-crud-list');
|
|
82
|
-
el.disableLoadOnStart = true;
|
|
83
|
-
el.config = config;
|
|
84
|
-
el.elements = sampleElements;
|
|
85
|
-
el.loading = false;
|
|
86
|
-
document.body.appendChild(el);
|
|
87
|
-
await el.updateComplete;
|
|
88
|
-
|
|
89
|
-
const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
|
|
90
|
-
expect(listItems.length).toBe(2);
|
|
91
|
-
|
|
92
|
-
for (const item of listItems) {
|
|
93
|
-
const actionsDiv = item.shadowRoot.querySelector('.actions');
|
|
94
|
-
expect(actionsDiv).toBeNull();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('renders custom grid template when templates.grid is configured', async () => {
|
|
99
|
-
let receivedElements = null;
|
|
100
|
-
let receivedActionIds = null;
|
|
101
|
-
let receivedSort = null;
|
|
102
|
-
|
|
103
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
104
|
-
templates: {
|
|
105
|
-
grid: (elements, actionIds, config, sort) => {
|
|
106
|
-
receivedElements = elements;
|
|
107
|
-
receivedActionIds = actionIds;
|
|
108
|
-
receivedSort = sort;
|
|
109
|
-
return html`
|
|
110
|
-
<dile-data-grid
|
|
111
|
-
.items="${elements}"
|
|
112
|
-
.selectedIds="${actionIds}"
|
|
113
|
-
selectable
|
|
114
|
-
.columns="${[
|
|
115
|
-
{ field: 'id', header: 'ID' },
|
|
116
|
-
{ field: 'name', header: 'Name' },
|
|
117
|
-
]}"
|
|
118
|
-
></dile-data-grid>
|
|
119
|
-
`;
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
}).getConfig();
|
|
123
|
-
|
|
124
|
-
const el = document.createElement('dile-crud-list');
|
|
125
|
-
el.disableLoadOnStart = true;
|
|
126
|
-
el.config = config;
|
|
127
|
-
el.elements = sampleElements;
|
|
128
|
-
el.actionIds = [2];
|
|
129
|
-
el.sort = { sortField: 'name', sortDirection: 'asc' };
|
|
130
|
-
el.loading = false;
|
|
131
|
-
document.body.appendChild(el);
|
|
132
|
-
await el.updateComplete;
|
|
133
|
-
|
|
134
|
-
const listItems = el.shadowRoot.querySelectorAll('dile-crud-list-item');
|
|
135
|
-
expect(listItems.length).toBe(0);
|
|
136
|
-
|
|
137
|
-
const gridContainer = el.shadowRoot.querySelector('.grid-container');
|
|
138
|
-
expect(gridContainer).toBeTruthy();
|
|
139
|
-
|
|
140
|
-
const dataGrid = el.shadowRoot.querySelector('dile-data-grid');
|
|
141
|
-
expect(dataGrid).toBeTruthy();
|
|
142
|
-
expect(receivedElements).toEqual(sampleElements);
|
|
143
|
-
expect(receivedActionIds).toEqual([2]);
|
|
144
|
-
expect(receivedSort).toEqual({ sortField: 'name', sortDirection: 'asc' });
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('updates sort when dile-data-grid-sort is dispatched', async () => {
|
|
148
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
149
|
-
templates: {
|
|
150
|
-
grid: (elements, actionIds) => html`
|
|
151
|
-
<dile-data-grid
|
|
152
|
-
.items="${elements}"
|
|
153
|
-
sort-mode="external"
|
|
154
|
-
.columns="${[
|
|
155
|
-
{ field: 'name', header: 'Name', sortable: true },
|
|
156
|
-
]}"
|
|
157
|
-
></dile-data-grid>
|
|
158
|
-
`,
|
|
159
|
-
},
|
|
160
|
-
}).getConfig();
|
|
161
|
-
|
|
162
|
-
const el = document.createElement('dile-crud-list');
|
|
163
|
-
el.disableLoadOnStart = true;
|
|
164
|
-
el.config = config;
|
|
165
|
-
el.elements = sampleElements;
|
|
166
|
-
el.loading = false;
|
|
167
|
-
document.body.appendChild(el);
|
|
168
|
-
await el.updateComplete;
|
|
169
|
-
|
|
170
|
-
let sortApplied = null;
|
|
171
|
-
el.setSort = (sortObj) => {
|
|
172
|
-
sortApplied = sortObj;
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const gridContainer = el.shadowRoot.querySelector('.grid-container');
|
|
176
|
-
gridContainer.dispatchEvent(
|
|
177
|
-
new CustomEvent('dile-data-grid-sort', {
|
|
178
|
-
bubbles: true,
|
|
179
|
-
composed: true,
|
|
180
|
-
detail: {
|
|
181
|
-
field: 'name',
|
|
182
|
-
direction: 'asc',
|
|
183
|
-
},
|
|
184
|
-
})
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
expect(sortApplied).toEqual({
|
|
188
|
-
sortField: 'name',
|
|
189
|
-
sortDirection: 'asc',
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('updates sort when dile-data-grid-sort is dispatched from the config.grid.columns branch', async () => {
|
|
194
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
195
|
-
grid: {
|
|
196
|
-
columns: [
|
|
197
|
-
{ field: 'name', header: 'Name', sortable: true },
|
|
198
|
-
],
|
|
199
|
-
},
|
|
200
|
-
}).getConfig();
|
|
201
|
-
|
|
202
|
-
const el = document.createElement('dile-crud-list');
|
|
203
|
-
el.disableLoadOnStart = true;
|
|
204
|
-
el.config = config;
|
|
205
|
-
el.elements = sampleElements;
|
|
206
|
-
el.loading = false;
|
|
207
|
-
document.body.appendChild(el);
|
|
208
|
-
await el.updateComplete;
|
|
209
|
-
|
|
210
|
-
let sortApplied = null;
|
|
211
|
-
el.setSort = (sortObj) => {
|
|
212
|
-
sortApplied = sortObj;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const gridContainer = el.shadowRoot.querySelector('.grid-container');
|
|
216
|
-
gridContainer.dispatchEvent(
|
|
217
|
-
new CustomEvent('dile-data-grid-sort', {
|
|
218
|
-
bubbles: true,
|
|
219
|
-
composed: true,
|
|
220
|
-
detail: {
|
|
221
|
-
field: 'name',
|
|
222
|
-
direction: 'desc',
|
|
223
|
-
},
|
|
224
|
-
})
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
expect(sortApplied).toEqual({
|
|
228
|
-
sortField: 'name',
|
|
229
|
-
sortDirection: 'desc',
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('prefers templates.grid over config.grid.columns when both are configured', async () => {
|
|
234
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
235
|
-
grid: {
|
|
236
|
-
columns: [
|
|
237
|
-
{ field: 'id', header: 'ID' },
|
|
238
|
-
],
|
|
239
|
-
},
|
|
240
|
-
templates: {
|
|
241
|
-
grid: (elements, actionIds) => html`
|
|
242
|
-
<dile-data-grid
|
|
243
|
-
.items="${elements}"
|
|
244
|
-
.columns="${[{ field: 'name', header: 'Name' }]}"
|
|
245
|
-
></dile-data-grid>
|
|
246
|
-
`,
|
|
247
|
-
},
|
|
248
|
-
}).getConfig();
|
|
249
|
-
|
|
250
|
-
const el = document.createElement('dile-crud-list');
|
|
251
|
-
el.disableLoadOnStart = true;
|
|
252
|
-
el.config = config;
|
|
253
|
-
el.elements = sampleElements;
|
|
254
|
-
el.loading = false;
|
|
255
|
-
document.body.appendChild(el);
|
|
256
|
-
await el.updateComplete;
|
|
257
|
-
|
|
258
|
-
expect(el.shadowRoot.querySelector('dile-crud-data-grid')).toBeNull();
|
|
259
|
-
expect(el.shadowRoot.querySelector('dile-data-grid')).toBeTruthy();
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('dispatches crud-list-sort-changed with the new sort whenever setSort is called', async () => {
|
|
263
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
264
|
-
grid: { columns: [{ field: 'name', header: 'Name', sortable: true }] },
|
|
265
|
-
}).getConfig();
|
|
266
|
-
|
|
267
|
-
const el = document.createElement('dile-crud-list');
|
|
268
|
-
el.disableLoadOnStart = true;
|
|
269
|
-
el.config = config;
|
|
270
|
-
el.elements = sampleElements;
|
|
271
|
-
el.loading = false;
|
|
272
|
-
document.body.appendChild(el);
|
|
273
|
-
await el.updateComplete;
|
|
274
|
-
|
|
275
|
-
let receivedDetail = null;
|
|
276
|
-
el.addEventListener('crud-list-sort-changed', (e) => {
|
|
277
|
-
receivedDetail = e.detail;
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
el.setSort({ sortField: 'name', sortDirection: 'desc' });
|
|
281
|
-
|
|
282
|
-
expect(receivedDetail).toEqual({ sortField: 'name', sortDirection: 'desc' });
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('keeps reporting the correct sort state to a freshly remounted grid after a loading round-trip', async () => {
|
|
286
|
-
// Reproduces the real scenario: setSort() sets loading=true, which unmounts
|
|
287
|
-
// elementsTemplate (and therefore <dile-crud-data-grid>/<dile-data-grid>) while
|
|
288
|
-
// the request is in flight, then getSuccess() remounts it from scratch.
|
|
289
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
290
|
-
grid: { columns: [{ field: 'name', header: 'Name', sortable: true }] },
|
|
291
|
-
}).getConfig();
|
|
292
|
-
|
|
293
|
-
const el = document.createElement('dile-crud-list');
|
|
294
|
-
el.disableLoadOnStart = true;
|
|
295
|
-
el.config = config;
|
|
296
|
-
el.elements = sampleElements;
|
|
297
|
-
el.loading = false;
|
|
298
|
-
document.body.appendChild(el);
|
|
299
|
-
await el.updateComplete;
|
|
300
|
-
|
|
301
|
-
el.elservice.setSort = () => {}; // avoid a real network round-trip
|
|
302
|
-
el.setSort({ sortField: 'name', sortDirection: 'asc' });
|
|
303
|
-
await el.updateComplete;
|
|
304
|
-
|
|
305
|
-
expect(el.shadowRoot.querySelector('.grid-container')).toBeNull(); // unmounted while loading
|
|
306
|
-
|
|
307
|
-
el.getSuccess({ detail: { elements: sampleElements, numItems: 2, paginationData: {} } });
|
|
308
|
-
await el.updateComplete;
|
|
309
|
-
|
|
310
|
-
const freshDataGrid = el.shadowRoot.querySelector('dile-crud-data-grid');
|
|
311
|
-
expect(freshDataGrid).toBeTruthy();
|
|
312
|
-
expect(freshDataGrid.sort).toEqual({ sortField: 'name', sortDirection: 'asc' });
|
|
313
|
-
|
|
314
|
-
const innerGrid = freshDataGrid.shadowRoot.querySelector('dile-data-grid');
|
|
315
|
-
await innerGrid.updateComplete;
|
|
316
|
-
expect(innerGrid.sortField).toBe('name');
|
|
317
|
-
expect(innerGrid.sortDirection).toBe('asc');
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('shows the item template by default when both templates.item and grid.columns are configured', async () => {
|
|
321
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
322
|
-
grid: { columns: [{ field: 'name', header: 'Name' }] },
|
|
323
|
-
templates: { item: (item) => html`<span class="item-name">${item.name}</span>` },
|
|
324
|
-
}).getConfig();
|
|
325
|
-
|
|
326
|
-
const el = document.createElement('dile-crud-list');
|
|
327
|
-
el.disableLoadOnStart = true;
|
|
328
|
-
el.config = config;
|
|
329
|
-
el.elements = sampleElements;
|
|
330
|
-
el.loading = false;
|
|
331
|
-
document.body.appendChild(el);
|
|
332
|
-
await el.updateComplete;
|
|
333
|
-
|
|
334
|
-
expect(el.shadowRoot.querySelectorAll('dile-crud-list-item').length).toBe(2);
|
|
335
|
-
expect(el.shadowRoot.querySelector('.grid-container')).toBeNull();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('shows the grid when viewMode is "grid" and both templates.item and grid.columns are configured', async () => {
|
|
339
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
340
|
-
grid: { columns: [{ field: 'name', header: 'Name' }] },
|
|
341
|
-
templates: { item: (item) => html`<span class="item-name">${item.name}</span>` },
|
|
342
|
-
}).getConfig();
|
|
343
|
-
|
|
344
|
-
const el = document.createElement('dile-crud-list');
|
|
345
|
-
el.disableLoadOnStart = true;
|
|
346
|
-
el.config = config;
|
|
347
|
-
el.elements = sampleElements;
|
|
348
|
-
el.loading = false;
|
|
349
|
-
el.viewMode = 'grid';
|
|
350
|
-
document.body.appendChild(el);
|
|
351
|
-
await el.updateComplete;
|
|
352
|
-
|
|
353
|
-
expect(el.shadowRoot.querySelectorAll('dile-crud-list-item').length).toBe(0);
|
|
354
|
-
expect(el.shadowRoot.querySelector('.grid-container')).toBeTruthy();
|
|
355
|
-
expect(el.shadowRoot.querySelector('dile-crud-data-grid')).toBeTruthy();
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it('still shows the grid when only grid.columns is configured, regardless of viewMode', async () => {
|
|
359
|
-
const config = new CrudConfigBuilder('https://example.test/api/customers', {
|
|
360
|
-
grid: { columns: [{ field: 'name', header: 'Name' }] },
|
|
361
|
-
}).getConfig();
|
|
362
|
-
|
|
363
|
-
const el = document.createElement('dile-crud-list');
|
|
364
|
-
el.disableLoadOnStart = true;
|
|
365
|
-
el.config = config;
|
|
366
|
-
el.elements = sampleElements;
|
|
367
|
-
el.loading = false;
|
|
368
|
-
document.body.appendChild(el);
|
|
369
|
-
await el.updateComplete;
|
|
370
|
-
|
|
371
|
-
expect(el.shadowRoot.querySelector('dile-crud-data-grid')).toBeTruthy();
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
describe('dile-crud-list scope changes and page reset', () => {
|
|
376
|
-
afterEach(() => {
|
|
377
|
-
document.body.innerHTML = '';
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
381
|
-
|
|
382
|
-
async function createList({ belongsTo, relationId } = {}) {
|
|
383
|
-
const config = new CrudConfigBuilder('https://example.test/api/invoices', {
|
|
384
|
-
templates: { item: (item) => html`<span>${item.name}</span>` },
|
|
385
|
-
}).getConfig();
|
|
386
|
-
|
|
387
|
-
const el = document.createElement('dile-crud-list');
|
|
388
|
-
el.disableLoadOnStart = true;
|
|
389
|
-
el.config = config;
|
|
390
|
-
if (belongsTo !== undefined) el.belongsTo = belongsTo;
|
|
391
|
-
if (relationId !== undefined) el.relationId = relationId;
|
|
392
|
-
document.body.appendChild(el);
|
|
393
|
-
await el.updateComplete;
|
|
394
|
-
await el.elservice.updateComplete;
|
|
395
|
-
|
|
396
|
-
// Intercept the outgoing requests instead of hitting the network.
|
|
397
|
-
const requests = [];
|
|
398
|
-
const ajax = el.elservice.ajaxget;
|
|
399
|
-
ajax.generateRequest = () => requests.push({ url: ajax.url, data: ajax.data });
|
|
400
|
-
el.elservice.delayTime = 10;
|
|
401
|
-
return { el, requests, ajax };
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
it('does not fire an extra request on the initial render when scope is set from the start', async () => {
|
|
405
|
-
const { requests } = await createList({ belongsTo: 'customer', relationId: '10' });
|
|
406
|
-
await wait(50);
|
|
407
|
-
expect(requests).toHaveLength(0);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it('reloads from the first page with the new scope when belongsTo and relationId change', async () => {
|
|
411
|
-
const { el, requests, ajax } = await createList({ belongsTo: 'customer', relationId: '10' });
|
|
412
|
-
ajax.url = 'https://example.test/api/invoices?page=3';
|
|
413
|
-
|
|
414
|
-
let refreshEvents = 0;
|
|
415
|
-
el.addEventListener('crud-list-refresh', () => refreshEvents++);
|
|
416
|
-
|
|
417
|
-
el.belongsTo = 'supplier';
|
|
418
|
-
el.relationId = '20';
|
|
419
|
-
await el.updateComplete;
|
|
420
|
-
expect(el.loading).toBe(true);
|
|
421
|
-
|
|
422
|
-
await wait(50);
|
|
423
|
-
expect(refreshEvents).toBe(1);
|
|
424
|
-
expect(requests).toHaveLength(1);
|
|
425
|
-
expect(requests[0].url).toBe('https://example.test/api/invoices');
|
|
426
|
-
expect(requests[0].data.belongsTo).toBe('supplier');
|
|
427
|
-
expect(requests[0].data.relationId).toBe('20');
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
it('reloads when only relationId changes', async () => {
|
|
431
|
-
const { el, requests } = await createList({ belongsTo: 'customer', relationId: '10' });
|
|
432
|
-
|
|
433
|
-
el.relationId = '11';
|
|
434
|
-
await el.updateComplete;
|
|
435
|
-
await wait(50);
|
|
436
|
-
|
|
437
|
-
expect(requests).toHaveLength(1);
|
|
438
|
-
expect(requests[0].data.relationId).toBe('11');
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
it('keeps the current page on refresh() and goes back to page 1 on refresh({ resetPage: true })', async () => {
|
|
442
|
-
const { el, requests, ajax } = await createList();
|
|
443
|
-
const pagedUrl = 'https://example.test/api/invoices?page=3';
|
|
444
|
-
|
|
445
|
-
ajax.url = pagedUrl;
|
|
446
|
-
el.refresh();
|
|
447
|
-
await wait(50);
|
|
448
|
-
expect(requests.at(-1).url).toBe(pagedUrl);
|
|
449
|
-
|
|
450
|
-
el.refresh({ resetPage: true });
|
|
451
|
-
await wait(50);
|
|
452
|
-
expect(requests.at(-1).url).toBe('https://example.test/api/invoices');
|
|
453
|
-
expect(requests).toHaveLength(2);
|
|
454
|
-
});
|
|
455
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
2
|
-
import './many-relation.js';
|
|
3
|
-
import './many-relation-overlay.js';
|
|
4
|
-
|
|
5
|
-
const VARIANTS = [
|
|
6
|
-
{ tag: 'dile-many-relation', selectTag: 'dile-ajax-select-crud' },
|
|
7
|
-
{ tag: 'dile-many-relation-overlay', selectTag: 'dile-ajax-select-crud-overlay' },
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
describe.each(VARIANTS)('$tag (select: $selectTag)', ({ tag, selectTag }) => {
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
document.body.innerHTML = '';
|
|
13
|
-
delete window.axiosInstance;
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
function mockAxios({ get = vi.fn(), post = vi.fn(), delete: del = vi.fn() } = {}) {
|
|
17
|
-
window.axiosInstance = { get, post, delete: del };
|
|
18
|
-
return window.axiosInstance;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function renderManyRelation(attrs = '') {
|
|
22
|
-
document.body.innerHTML = `
|
|
23
|
-
<${tag}
|
|
24
|
-
endpointGet="/api/tags"
|
|
25
|
-
endpointList="/api/board-games/1/tags"
|
|
26
|
-
endpointAdd="/api/board-games/1/tags"
|
|
27
|
-
endpointRemove="/api/board-games/1/tags"
|
|
28
|
-
idProperty="id"
|
|
29
|
-
displayProperty="name"
|
|
30
|
-
${attrs}
|
|
31
|
-
></${tag}>
|
|
32
|
-
`;
|
|
33
|
-
const el = document.body.querySelector(tag);
|
|
34
|
-
await el.updateComplete;
|
|
35
|
-
return el;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function select(el) {
|
|
39
|
-
return el.shadowRoot.getElementById('theselect');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
it(`renders a ${selectTag} as the select control`, async () => {
|
|
43
|
-
mockAxios({ get: vi.fn(() => Promise.resolve({ status: 200, data: [] })) });
|
|
44
|
-
const el = await renderManyRelation();
|
|
45
|
-
|
|
46
|
-
const theselect = select(el);
|
|
47
|
-
expect(theselect).toBeTruthy();
|
|
48
|
-
expect(theselect.tagName.toLowerCase()).toBe(selectTag);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('passes iconProperty and icon through to the internal select', async () => {
|
|
52
|
-
mockAxios({ get: vi.fn(() => Promise.resolve({ status: 200, data: [] })) });
|
|
53
|
-
const el = await renderManyRelation('iconProperty="iconName" icon="lucide.globe"');
|
|
54
|
-
|
|
55
|
-
const theselect = select(el);
|
|
56
|
-
expect(theselect.getAttribute('iconproperty')).toBe('iconName');
|
|
57
|
-
expect(theselect.getAttribute('icon')).toBe('lucide.globe');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('loads the related items list from endpointList on connect', async () => {
|
|
61
|
-
const get = vi.fn(() =>
|
|
62
|
-
Promise.resolve({ status: 200, data: { data: [{ id: 1, name: 'Strategy' }] } })
|
|
63
|
-
);
|
|
64
|
-
mockAxios({ get });
|
|
65
|
-
const el = await renderManyRelation('loadFromEndpoint');
|
|
66
|
-
await vi.waitFor(() => expect(el._items).toEqual([{ id: 1, name: 'Strategy' }]));
|
|
67
|
-
|
|
68
|
-
expect(get).toHaveBeenCalledWith('/api/board-games/1/tags', { params: undefined });
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('adds the item selected through the select control', async () => {
|
|
72
|
-
const post = vi.fn(() => Promise.resolve({ status: 201, data: {} }));
|
|
73
|
-
const get = vi.fn(() => Promise.resolve({ status: 200, data: { data: [] } }));
|
|
74
|
-
mockAxios({ post, get });
|
|
75
|
-
const el = await renderManyRelation();
|
|
76
|
-
|
|
77
|
-
select(el).dispatchEvent(
|
|
78
|
-
new CustomEvent('element-changed', { detail: { value: '5' } })
|
|
79
|
-
);
|
|
80
|
-
await el.updateComplete;
|
|
81
|
-
expect(el._selectedId).toBe('5');
|
|
82
|
-
|
|
83
|
-
el.shadowRoot.querySelector('button.add-btn').click();
|
|
84
|
-
// adding successfully triggers a list auto-refresh; wait it out before mocks are torn down
|
|
85
|
-
await vi.waitFor(() => expect(get).toHaveBeenCalled());
|
|
86
|
-
|
|
87
|
-
expect(post).toHaveBeenCalledWith('/api/board-games/1/tags', { id: '5' }, undefined);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('removes an item and dispatches many-relation-remove-success', async () => {
|
|
91
|
-
const del = vi.fn(() => Promise.resolve({ status: 200, data: {} }));
|
|
92
|
-
const get = vi.fn(() => Promise.resolve({ status: 200, data: { data: [] } }));
|
|
93
|
-
mockAxios({ get, delete: del });
|
|
94
|
-
const el = await renderManyRelation();
|
|
95
|
-
el.relatedItems = [{ id: 9, name: 'Party' }];
|
|
96
|
-
|
|
97
|
-
const removeListener = vi.fn();
|
|
98
|
-
el.addEventListener('many-relation-remove-success', removeListener);
|
|
99
|
-
|
|
100
|
-
await vi.waitFor(() =>
|
|
101
|
-
expect(el.shadowRoot.querySelector('button.remove-btn')).toBeTruthy()
|
|
102
|
-
);
|
|
103
|
-
el.shadowRoot.querySelector('button.remove-btn').click();
|
|
104
|
-
await vi.waitFor(() => expect(removeListener).toHaveBeenCalled());
|
|
105
|
-
// wait out the list auto-refresh triggered by the removal before mocks are torn down
|
|
106
|
-
await vi.waitFor(() => expect(get).toHaveBeenCalled());
|
|
107
|
-
|
|
108
|
-
expect(del).toHaveBeenCalledWith('/api/board-games/1/tags/9', {}, undefined);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('dile-many-relation-overlay icons (only the overlay select supports them)', () => {
|
|
113
|
-
afterEach(() => {
|
|
114
|
-
document.body.innerHTML = '';
|
|
115
|
-
delete window.axiosInstance;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
function wait(ms) {
|
|
119
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
it('reaches the internal dile-select-overlay popup as data-icon on the generated options', async () => {
|
|
123
|
-
const get = vi.fn(() =>
|
|
124
|
-
Promise.resolve({ status: 200, data: [{ id: 1, name: 'Strategy', iconName: 'material.star' }] })
|
|
125
|
-
);
|
|
126
|
-
window.axiosInstance = { get };
|
|
127
|
-
|
|
128
|
-
document.body.innerHTML = `
|
|
129
|
-
<dile-many-relation-overlay
|
|
130
|
-
endpointGet="/api/tags"
|
|
131
|
-
endpointList="/api/board-games/1/tags"
|
|
132
|
-
endpointAdd="/api/board-games/1/tags"
|
|
133
|
-
endpointRemove="/api/board-games/1/tags"
|
|
134
|
-
idProperty="id"
|
|
135
|
-
displayProperty="name"
|
|
136
|
-
iconProperty="iconName"
|
|
137
|
-
icon="lucide.globe"
|
|
138
|
-
></dile-many-relation-overlay>
|
|
139
|
-
`;
|
|
140
|
-
const el = document.body.querySelector('dile-many-relation-overlay');
|
|
141
|
-
await el.updateComplete;
|
|
142
|
-
|
|
143
|
-
const theselect = el.shadowRoot.getElementById('theselect');
|
|
144
|
-
const input = theselect.shadowRoot.getElementById('search');
|
|
145
|
-
input.value = 'str';
|
|
146
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
147
|
-
await wait(theselect.delay + 50);
|
|
148
|
-
await theselect.updateComplete;
|
|
149
|
-
|
|
150
|
-
const options = theselect.select.options;
|
|
151
|
-
// Only result has its own iconName, taking precedence over the select-level default.
|
|
152
|
-
expect(options[0].icon).toBe('material.star');
|
|
153
|
-
});
|
|
154
|
-
});
|