@dile/crud 2.8.5 → 2.8.7
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/action/src/DileCrudSingleActions.js +30 -2
- package/components/action/src/DileCrudSingleActions.spec.js +126 -0
- 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/single/src/DileCrudSingle.js +6 -2
- package/components/single/src/DileCrudSingle.spec.js +151 -0
- 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/package.json +3 -3
|
@@ -47,6 +47,7 @@ export class DileCrudSingleActions extends DileCrudActions {
|
|
|
47
47
|
super();
|
|
48
48
|
this.actions = [];
|
|
49
49
|
this.directSingleActions = [];
|
|
50
|
+
this._initialData = null;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
get actionListTemplate() {
|
|
@@ -94,14 +95,41 @@ export class DileCrudSingleActions extends DileCrudActions {
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
async showAction() {
|
|
99
|
+
super.showAction();
|
|
100
|
+
// Wait for the form to be rendered after the confirm opens
|
|
101
|
+
let form = this.selectedActionForm;
|
|
102
|
+
let attempts = 0;
|
|
103
|
+
while (!form && attempts < 50) {
|
|
104
|
+
await this.updateComplete;
|
|
105
|
+
await new Promise(r => setTimeout(r, 10));
|
|
106
|
+
form = this.selectedActionForm;
|
|
107
|
+
attempts++;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (form) {
|
|
111
|
+
if (this._initialData) {
|
|
112
|
+
await form.setData(this._initialData);
|
|
113
|
+
this._initialData = null;
|
|
114
|
+
} else {
|
|
115
|
+
// Reset form if no initial data
|
|
116
|
+
await form.resetData();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async triggerAction(actionName, initialData) {
|
|
98
122
|
this.selection = actionName;
|
|
99
123
|
this.computeDestructive();
|
|
100
124
|
const action = this.actions.find(a => a.name === actionName);
|
|
101
125
|
if (action && action.onClick) {
|
|
102
126
|
action.onClick(this.element);
|
|
103
127
|
} else {
|
|
104
|
-
|
|
128
|
+
// Store initial data for showAction to apply
|
|
129
|
+
if (initialData) {
|
|
130
|
+
this._initialData = initialData;
|
|
131
|
+
}
|
|
132
|
+
await this.showAction();
|
|
105
133
|
}
|
|
106
134
|
}
|
|
107
135
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
|
|
Binary file
|
|
@@ -174,8 +174,12 @@ export class DileCrudSingle extends DileI18nMixin(DileCrudMixin(LitElement)) {
|
|
|
174
174
|
this.detailElement.refresh();
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
triggerDirectAction(actionName) {
|
|
178
|
-
this.singleActionsElement.triggerAction(actionName);
|
|
177
|
+
triggerDirectAction(actionName, initialData) {
|
|
178
|
+
this.singleActionsElement.triggerAction(actionName, initialData);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
triggerAction(actionName, initialData) {
|
|
182
|
+
this.singleActionsElement.triggerAction(actionName, initialData);
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
elementLoaded(e) {
|
|
@@ -0,0 +1,151 @@
|
|
|
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.js';
|
|
5
|
+
|
|
6
|
+
// Create a test form component
|
|
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('DileCrudSingle.triggerAction and triggerDirectAction', () => {
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
document.body.innerHTML = '';
|
|
23
|
+
delete window.axiosInstance;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function mockAxios({ get = vi.fn(() => Promise.resolve({ status: 200, data: {} })) } = {}) {
|
|
27
|
+
window.axiosInstance = { get };
|
|
28
|
+
return window.axiosInstance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const testActions = [
|
|
32
|
+
{
|
|
33
|
+
name: 'CreateInvoiceAction',
|
|
34
|
+
label: 'Create Invoice',
|
|
35
|
+
destructive: false,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function createFormTemplate() {
|
|
40
|
+
return (selection, element) => html`
|
|
41
|
+
${selection === 'CreateInvoiceAction'
|
|
42
|
+
? html`<test-action-form action="CreateInvoiceAction"></test-action-form>`
|
|
43
|
+
: ''
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const testConfig = {
|
|
49
|
+
endpoint: 'https://example.test/api/items',
|
|
50
|
+
actions: {
|
|
51
|
+
single: testActions,
|
|
52
|
+
directSingleActions: [],
|
|
53
|
+
list: [],
|
|
54
|
+
},
|
|
55
|
+
customization: {
|
|
56
|
+
disableEdit: true,
|
|
57
|
+
disableListActionsOnSingleComponent: true,
|
|
58
|
+
},
|
|
59
|
+
labels: {
|
|
60
|
+
startUpdateAction: 'Edit',
|
|
61
|
+
},
|
|
62
|
+
templates: {
|
|
63
|
+
detail: (item) => html`<div class="item-detail">${item?.name || ''}</div>`,
|
|
64
|
+
formSingleActions: createFormTemplate(),
|
|
65
|
+
relations: () => html``,
|
|
66
|
+
updateForm: () => html``,
|
|
67
|
+
},
|
|
68
|
+
formIds: {
|
|
69
|
+
updateForm: 'update-form',
|
|
70
|
+
},
|
|
71
|
+
translations: {
|
|
72
|
+
element_actions: 'Actions',
|
|
73
|
+
},
|
|
74
|
+
responseAdapter: {
|
|
75
|
+
setResponse: () => {},
|
|
76
|
+
getData: () => ({ id: 1, name: 'Test Item' }),
|
|
77
|
+
getActionResponseMessage: () => 'Success',
|
|
78
|
+
getActionResponseActionName: () => 'CreateInvoiceAction',
|
|
79
|
+
getActionResponseActionData: () => ({}),
|
|
80
|
+
},
|
|
81
|
+
destructiveActionNames: [],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
async function createCrudSingle() {
|
|
85
|
+
mockAxios({
|
|
86
|
+
get: vi.fn(() => Promise.resolve({
|
|
87
|
+
status: 200,
|
|
88
|
+
data: { data: { id: 1, name: 'Test Item' } }
|
|
89
|
+
}))
|
|
90
|
+
});
|
|
91
|
+
const el = document.createElement('dile-crud-single');
|
|
92
|
+
el.relatedId = '1';
|
|
93
|
+
el.config = testConfig;
|
|
94
|
+
el.element = { id: 1, name: 'Test Item' };
|
|
95
|
+
document.body.appendChild(el);
|
|
96
|
+
await el.updateComplete;
|
|
97
|
+
return el;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
it('triggerAction delegates to singleActionsElement.triggerAction', async () => {
|
|
101
|
+
const el = await createCrudSingle();
|
|
102
|
+
|
|
103
|
+
const singleActionsElement = el.singleActionsElement;
|
|
104
|
+
const triggerActionSpy = vi.spyOn(singleActionsElement, 'triggerAction');
|
|
105
|
+
|
|
106
|
+
const initialData = { productUuid: 'uuid-123', units: 5 };
|
|
107
|
+
el.triggerAction('CreateInvoiceAction', initialData);
|
|
108
|
+
|
|
109
|
+
expect(triggerActionSpy).toHaveBeenCalledWith('CreateInvoiceAction', initialData);
|
|
110
|
+
triggerActionSpy.mockRestore();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('triggerDirectAction delegates to singleActionsElement.triggerAction', async () => {
|
|
114
|
+
const el = await createCrudSingle();
|
|
115
|
+
|
|
116
|
+
const singleActionsElement = el.singleActionsElement;
|
|
117
|
+
const triggerActionSpy = vi.spyOn(singleActionsElement, 'triggerAction');
|
|
118
|
+
|
|
119
|
+
const initialData = { productUuid: 'uuid-456', units: 2 };
|
|
120
|
+
el.triggerDirectAction('CreateInvoiceAction', initialData);
|
|
121
|
+
|
|
122
|
+
expect(triggerActionSpy).toHaveBeenCalledWith('CreateInvoiceAction', initialData);
|
|
123
|
+
triggerActionSpy.mockRestore();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('triggerDirectAction without initialData still works (backward compatibility)', async () => {
|
|
127
|
+
const el = await createCrudSingle();
|
|
128
|
+
|
|
129
|
+
const singleActionsElement = el.singleActionsElement;
|
|
130
|
+
const triggerActionSpy = vi.spyOn(singleActionsElement, 'triggerAction');
|
|
131
|
+
|
|
132
|
+
el.triggerDirectAction('CreateInvoiceAction');
|
|
133
|
+
|
|
134
|
+
expect(triggerActionSpy).toHaveBeenCalledWith('CreateInvoiceAction', undefined);
|
|
135
|
+
triggerActionSpy.mockRestore();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('triggerAction without initialData works', async () => {
|
|
139
|
+
const el = await createCrudSingle();
|
|
140
|
+
|
|
141
|
+
const singleActionsElement = el.singleActionsElement;
|
|
142
|
+
const triggerActionSpy = vi.spyOn(singleActionsElement, 'triggerAction');
|
|
143
|
+
|
|
144
|
+
el.triggerAction('CreateInvoiceAction');
|
|
145
|
+
|
|
146
|
+
expect(triggerActionSpy).toHaveBeenCalledWith('CreateInvoiceAction', undefined);
|
|
147
|
+
triggerActionSpy.mockRestore();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/crud",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.7",
|
|
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.
|
|
28
|
+
"@dile/ui": "^3.14.0",
|
|
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": "
|
|
35
|
+
"gitHead": "2dbcfcfc126715e1f21c28806714f5f59b89d93b"
|
|
36
36
|
}
|