@dile/crud 2.10.0 → 2.10.2

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.
Files changed (42) hide show
  1. package/package.json +3 -3
  2. package/components/action/src/DileCrudSingleActions.spec.js +0 -126
  3. 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
  4. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-initialData-is-cleared-after-being-applied-1.png +0 -0
  5. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-initialData-is-only-applied-once-and-cleared-after-opening-1.png +0 -0
  6. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-behavior-matches-original-when-no-initialData-1.png +0 -0
  7. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-opens-confirm-dialog-1.png +0 -0
  8. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-with-initialData-sets-form-values-1.png +0 -0
  9. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-with-initialData-sets-form-values-before-opening-dialog-1.png +0 -0
  10. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-without-initialData-resets-form-1.png +0 -0
  11. package/components/action/src/__screenshots__/DileCrudSingleActions.spec.js/DileCrudSingleActions-with-triggerAction-triggerAction-without-initialData-resets-form-to-empty-1.png +0 -0
  12. package/components/ajax/ajax.spec.js +0 -35
  13. package/components/ajax-select-crud/ajax-select-crud-overlay.spec.js +0 -186
  14. 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
  15. 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
  16. package/components/crud/crud.spec.js +0 -453
  17. package/components/file-uploader/file-uploader.spec.js +0 -32
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. package/components/list/crud-data-grid.spec.js +0 -207
  25. package/components/list/crud-item-actions.spec.js +0 -122
  26. package/components/list/crud-list.spec.js +0 -455
  27. package/components/many-relation/many-relation.spec.js +0 -154
  28. package/components/select-ajax-simple/select-ajax-simple.spec.js +0 -133
  29. package/components/single/src/DileCrudSingle.spec.js +0 -151
  30. package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerAction-delegates-to-singleActionsElement-triggerAction-1.png +0 -0
  31. package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerAction-without-initialData-works-1.png +0 -0
  32. package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerDirectAction-delegates-to-singleActionsElement-triggerAction-1.png +0 -0
  33. package/components/single/src/__screenshots__/DileCrudSingle.spec.js/DileCrudSingle-triggerAction-and-triggerDirectAction-triggerDirectAction-without-initialData-still-works--backward-compatibility--1.png +0 -0
  34. package/components/steps-form/__screenshots__/steps-form.spec.js/dile-steps-form-translates-the-navigation-labels-with-the-language-property-1.png +0 -0
  35. package/components/steps-form/steps-form.spec.js +0 -230
  36. 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
  37. 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
  38. 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
  39. package/components/ui/crud-sort-form.spec.js +0 -132
  40. package/lib/CrudConfigBuilder.spec.js +0 -32
  41. package/lib/deepMerge.spec.js +0 -45
  42. package/lib/listViewMode.spec.js +0 -73
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dile/crud",
3
- "version": "2.10.0",
3
+ "version": "2.10.2",
4
4
  "description": "Components to create a generic crud system based on Web Components and Lit",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,12 +25,12 @@
25
25
  },
26
26
  "homepage": "https://dile-components.com/",
27
27
  "dependencies": {
28
- "@dile/ui": "^3.18.0",
28
+ "@dile/ui": "^3.18.2",
29
29
  "axios": "^1.19.0",
30
30
  "lit": "^2.7.0 || ^3.0.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "gitHead": "1b065736666cc26f8afc1106ec7ffc98feaec60e"
35
+ "gitHead": "b59afd879d78118d5ea1cdc53e144f1a84628f13"
36
36
  }
@@ -1,126 +0,0 @@
1
- import { describe, it, expect, afterEach, vi } from 'vitest';
2
- import { html, LitElement } from 'lit';
3
- import { DileForm } from '@dile/ui/mixins/form';
4
- import '../crud-single-actions.js';
5
-
6
- // Create a test form component that extends DileForm
7
- class TestActionForm extends DileForm(LitElement) {
8
- render() {
9
- return html`
10
- <input name="productUuid" />
11
- <input name="units" type="number" value="1" />
12
- `;
13
- }
14
- }
15
-
16
- if (!customElements.get('test-action-form')) {
17
- customElements.define('test-action-form', TestActionForm);
18
- }
19
-
20
- describe('DileCrudSingleActions with triggerAction', () => {
21
- afterEach(() => {
22
- document.body.innerHTML = '';
23
- });
24
-
25
- const testActions = [
26
- {
27
- name: 'CreateInvoiceAction',
28
- label: 'Create Invoice',
29
- destructive: false,
30
- },
31
- ];
32
-
33
- function createFormTemplate(selection) {
34
- return (selection, element) => html`
35
- ${selection === 'CreateInvoiceAction'
36
- ? html`<test-action-form action="CreateInvoiceAction"></test-action-form>`
37
- : ''
38
- }
39
- `;
40
- }
41
-
42
- async function createSingleActions() {
43
- const el = document.createElement('dile-crud-single-actions');
44
- el.actions = testActions;
45
- el.directSingleActions = [];
46
- el.formActionsTemplate = createFormTemplate(testActions[0].name);
47
- el.actionIds = [1];
48
- el.element = { id: 1, name: 'Test Item' };
49
- el.endpoint = 'https://example.test/api';
50
- el.language = 'en';
51
- document.body.appendChild(el);
52
- await el.updateComplete;
53
- return el;
54
- }
55
-
56
- async function waitForForm(el, maxWait = 2000) {
57
- const startTime = Date.now();
58
- while (Date.now() - startTime < maxWait) {
59
- const form = el.selectedActionForm;
60
- if (form) {
61
- await form.updateComplete;
62
- return form;
63
- }
64
- await new Promise(r => setTimeout(r, 50));
65
- }
66
- return null;
67
- }
68
-
69
- it('triggerAction with initialData sets form values', async () => {
70
- const el = await createSingleActions();
71
-
72
- const initialData = { productUuid: 'uuid-123', units: '5' };
73
- el.triggerAction('CreateInvoiceAction', initialData);
74
-
75
- const form = await waitForForm(el);
76
- expect(form).toBeTruthy();
77
-
78
- const productInput = form.shadowRoot.querySelector('[name="productUuid"]');
79
- const unitsInput = form.shadowRoot.querySelector('[name="units"]');
80
-
81
- expect(productInput.value).toBe('uuid-123');
82
- expect(unitsInput.value).toBe('5');
83
- });
84
-
85
- it('triggerAction without initialData resets form to empty', async () => {
86
- const el = await createSingleActions();
87
-
88
- // First open with initial data
89
- el.triggerAction('CreateInvoiceAction', { productUuid: 'uuid-old', units: '10' });
90
- let form = await waitForForm(el);
91
- expect(form.shadowRoot.querySelector('[name="productUuid"]').value).toBe('uuid-old');
92
-
93
- // Close the dialog
94
- const confirm = el.confirmElement;
95
- confirm.close();
96
- await new Promise(r => setTimeout(r, 100));
97
-
98
- // Open again without initial data - should reset
99
- el.triggerAction('CreateInvoiceAction');
100
- form = await waitForForm(el);
101
-
102
- // Form should be empty after reset
103
- expect(form.shadowRoot.querySelector('[name="productUuid"]').value).toBe('');
104
- });
105
-
106
- it('triggerAction opens confirm dialog', async () => {
107
- const el = await createSingleActions();
108
-
109
- el.triggerAction('CreateInvoiceAction');
110
- await waitForForm(el);
111
-
112
- const confirm = el.confirmElement;
113
- expect(confirm).toBeTruthy();
114
- });
115
-
116
- it('initialData is cleared after being applied', async () => {
117
- const el = await createSingleActions();
118
-
119
- el.triggerAction('CreateInvoiceAction', { productUuid: 'uuid-first' });
120
- await waitForForm(el);
121
-
122
- // _initialData should be null after being used
123
- expect(el._initialData).toBeNull();
124
- });
125
- });
126
-
@@ -1,35 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import './ajax.js';
3
-
4
- describe('dile-ajax', () => {
5
- afterEach(() => {
6
- document.body.innerHTML = '';
7
- });
8
-
9
- it('appends arrays of files as multiple "key[]" entries when sending data as FormData', async () => {
10
- const el = document.createElement('dile-ajax');
11
- document.body.appendChild(el);
12
- await el.updateComplete;
13
-
14
- const files = [new File(['a'], 'a.txt'), new File(['b'], 'b.txt')];
15
- el.data = { title: 'Docs', files, empty: null };
16
- el._prepareFormData();
17
-
18
- expect(el.formData.get('title')).toBe('Docs');
19
- expect(el.formData.getAll('files[]').map(file => file.name)).toEqual(['a.txt', 'b.txt']);
20
- expect(el.formData.has('files')).toBe(false);
21
- expect(el.formData.has('empty')).toBe(false);
22
- });
23
-
24
- it('keeps sending other arrays as a single entry', async () => {
25
- const el = document.createElement('dile-ajax');
26
- document.body.appendChild(el);
27
- await el.updateComplete;
28
-
29
- el.data = { tags: [1, 2, 3] };
30
- el._prepareFormData();
31
-
32
- expect(el.formData.get('tags')).toBe('1,2,3');
33
- expect(el.formData.has('tags[]')).toBe(false);
34
- });
35
- });
@@ -1,186 +0,0 @@
1
- import { describe, it, expect, afterEach, vi } from 'vitest';
2
- import './ajax-select-crud-overlay.js';
3
-
4
- describe('dile-ajax-select-crud-overlay', () => {
5
- afterEach(() => {
6
- document.body.innerHTML = '';
7
- delete window.axiosInstance;
8
- });
9
-
10
- function wait(ms) {
11
- return new Promise((resolve) => setTimeout(resolve, ms));
12
- }
13
-
14
- function mockAxios(responseData) {
15
- const get = vi.fn(() => Promise.resolve({ status: 200, data: responseData }));
16
- window.axiosInstance = { get };
17
- return get;
18
- }
19
-
20
- async function renderCrudSelect(html) {
21
- document.body.innerHTML = html;
22
- const el = document.body.querySelector('dile-ajax-select-crud-overlay');
23
- await el.updateComplete;
24
- return el;
25
- }
26
-
27
- async function typeKeyword(el, keyword) {
28
- const input = el.shadowRoot.getElementById('search');
29
- input.value = keyword;
30
- input.dispatchEvent(new Event('input', { bubbles: true }));
31
- await wait(el.delay + 50);
32
- await el.updateComplete;
33
- }
34
-
35
- describe('Rendering', () => {
36
- it('renders the search input, same as dile-select-ajax-overlay', async () => {
37
- mockAxios([]);
38
- const el = await renderCrudSelect(`
39
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name"></dile-ajax-select-crud-overlay>
40
- `);
41
-
42
- const input = el.shadowRoot.getElementById('search');
43
- expect(input).toBeTruthy();
44
- expect(input.getAttribute('role')).toBe('combobox');
45
- });
46
- });
47
-
48
- describe('Uses axios instead of fetch', () => {
49
- it('requests through window.axiosInstance.get, not the native fetch API', async () => {
50
- const get = mockAxios([{ id: 1, name: 'Spain' }]);
51
- const fetchSpy = vi.spyOn(window, 'fetch');
52
-
53
- const el = await renderCrudSelect(`
54
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" queryStringVariable="keyword" delay="10"></dile-ajax-select-crud-overlay>
55
- `);
56
-
57
- await typeKeyword(el, 'sp');
58
-
59
- expect(get).toHaveBeenCalledWith('/api/countries', { params: { keyword: 'sp' } });
60
- expect(fetchSpy).not.toHaveBeenCalled();
61
- expect(el.data.length).toBe(1);
62
- expect(el.select.opened).toBe(true);
63
-
64
- fetchSpy.mockRestore();
65
- });
66
-
67
- it('fetches the initial value display text through axios', async () => {
68
- // searchValueInitial() passes response.data (i.e. the "data" envelope, not the raw
69
- // response) down to registerText(), matching dile-ajax-select-crud's own convention.
70
- const get = vi.fn(() => Promise.resolve({ status: 200, data: { data: { name: 'Spain' } } }));
71
- window.axiosInstance = { get };
72
-
73
- const el = await renderCrudSelect(`
74
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" value="22"></dile-ajax-select-crud-overlay>
75
- `);
76
- await wait(50);
77
- await el.updateComplete;
78
-
79
- expect(get).toHaveBeenCalledWith('/api/countries/22');
80
- expect(el.selectedText).toBe('Spain');
81
- expect(el.isSelected).toBe(true);
82
- });
83
- });
84
-
85
- describe('Selecting a result (no extra click required)', () => {
86
- it('opens the popup as soon as results load and lets the user pick without a second click', async () => {
87
- mockAxios([{ id: 1, name: 'Spain' }, { id: 2, name: 'France' }]);
88
- const el = await renderCrudSelect(`
89
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" delay="10"></dile-ajax-select-crud-overlay>
90
- `);
91
-
92
- await typeKeyword(el, 'sp');
93
- expect(el.select.opened).toBe(true);
94
-
95
- const options = el.select.shadowRoot.querySelectorAll('[role="option"]');
96
- options[0].click();
97
- await el.select.updateComplete;
98
- await el.updateComplete;
99
-
100
- expect(el.value).toBe('1');
101
- expect(el.selectedText).toBe('Spain');
102
- expect(el.isSelected).toBe(true);
103
- });
104
- });
105
-
106
- describe('maxResults / pageParamName', () => {
107
- it('adds the page-size param to the request when both are set', async () => {
108
- const get = mockAxios([]);
109
- const el = await renderCrudSelect(`
110
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" queryStringVariable="keyword" maxResults="5" pageParamName="limit" delay="10"></dile-ajax-select-crud-overlay>
111
- `);
112
-
113
- await typeKeyword(el, 'sp');
114
-
115
- expect(get).toHaveBeenCalledWith('/api/countries', { params: { keyword: 'sp', limit: 5 } });
116
- });
117
-
118
- it('omits the page-size param when pageParamName is not set', async () => {
119
- const get = mockAxios([]);
120
- const el = await renderCrudSelect(`
121
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" queryStringVariable="keyword" maxResults="5" delay="10"></dile-ajax-select-crud-overlay>
122
- `);
123
-
124
- await typeKeyword(el, 'sp');
125
-
126
- expect(get).toHaveBeenCalledWith('/api/countries', { params: { keyword: 'sp' } });
127
- });
128
- });
129
-
130
- describe('getSelectResultList', () => {
131
- it('uses getSelectResultList to extract the list when the response shape needs adapting', async () => {
132
- mockAxios({ data: { result: { data: [{ id: 1, name: 'Spain' }] } } });
133
- const el = await renderCrudSelect(`
134
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" delay="10"></dile-ajax-select-crud-overlay>
135
- `);
136
- el.getSelectResultList = (response) => response.data.result.data;
137
-
138
- await typeKeyword(el, 'sp');
139
-
140
- expect(el.data).toEqual([{ id: 1, name: 'Spain' }]);
141
- });
142
-
143
- it('takes priority over resultDataProperty when both are set', async () => {
144
- mockAxios({ items: [{ id: 1, name: 'Spain' }], data: [] });
145
- const el = await renderCrudSelect(`
146
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" resultDataProperty="data" delay="10"></dile-ajax-select-crud-overlay>
147
- `);
148
- el.getSelectResultList = (response) => response.items;
149
-
150
- await typeKeyword(el, 'sp');
151
-
152
- expect(el.data).toEqual([{ id: 1, name: 'Spain' }]);
153
- });
154
- });
155
-
156
- describe('Icons (inherited from dile-select-ajax-overlay)', () => {
157
- it('propagates iconProperty and icon to data-icon on the generated options/select', async () => {
158
- mockAxios([
159
- { id: 1, name: 'Spain' },
160
- { id: 2, name: 'France', iconName: 'material.star' },
161
- ]);
162
- const el = await renderCrudSelect(`
163
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name" iconProperty="iconName" icon="lucide.globe" delay="10"></dile-ajax-select-crud-overlay>
164
- `);
165
-
166
- await typeKeyword(el, 'sp');
167
-
168
- const options = el.select.options;
169
- // Spain has no iconName of its own, so it falls back to the select-level default.
170
- expect(options[0].icon).toBe('lucide.globe');
171
- // France declares its own icon via iconProperty, which takes precedence.
172
- expect(options[1].icon).toBe('material.star');
173
- });
174
- });
175
-
176
- describe('Form Association', () => {
177
- it('is form-associated, same as dile-select-ajax-overlay', async () => {
178
- mockAxios([]);
179
- const el = await renderCrudSelect(`
180
- <dile-ajax-select-crud-overlay endpoint="/api/countries" idProperty="id" displayProperty="name"></dile-ajax-select-crud-overlay>
181
- `);
182
-
183
- expect(el.internals).toBeTruthy();
184
- });
185
- });
186
- });