@osimatic/helpers-js 1.5.3 → 1.5.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/details_sub_array.js +45 -41
- package/form_helper.js +283 -232
- package/google_charts.js +154 -144
- package/google_maps.js +1 -1
- package/import_from_csv.js +166 -157
- package/multi_files_input.js +42 -34
- package/multiple_action_in_table.js +116 -105
- package/package.json +1 -1
- package/paging.js +103 -84
- package/select_all.js +65 -70
- package/sortable_list.js +12 -13
- package/tests/details_sub_array.test.js +211 -239
- package/tests/form_helper.test.js +553 -673
- package/tests/google_charts.test.js +338 -339
- package/tests/google_maps.test.js +3 -15
- package/tests/import_from_csv.test.js +391 -652
- package/tests/multi_files_input.test.js +292 -722
- package/tests/multiple_action_in_table.test.js +458 -417
- package/tests/paging.test.js +344 -475
- package/tests/select_all.test.js +232 -318
- package/tests/sortable_list.test.js +176 -500
- package/tests/user.test.js +163 -54
- package/user.js +35 -38
|
@@ -3,466 +3,507 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { MultipleActionInTable, MultipleActionInDivList } = require('../multiple_action_in_table');
|
|
5
5
|
|
|
6
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
function setupTable({ nbRows = 1, withButtonsDiv = true, nested = false, tableClass = 'table-action_multiple' } = {}) {
|
|
9
|
+
const rows = Array.from({ length: nbRows }, (_, i) => `
|
|
10
|
+
<tr data-action_multiple_input_name="ids[]" data-action_multiple_item_id="${i + 1}">
|
|
11
|
+
<td>Item ${i + 1}</td>
|
|
12
|
+
</tr>`).join('');
|
|
13
|
+
|
|
14
|
+
const buttonsDiv = withButtonsDiv
|
|
15
|
+
? `<div class="action_multiple_buttons hide"><button>Action</button></div>`
|
|
16
|
+
: '';
|
|
17
|
+
|
|
18
|
+
if (nested) {
|
|
19
|
+
// table.parentElement.parentElement.parentElement.nextElementSibling = buttons div
|
|
20
|
+
document.body.innerHTML = `
|
|
21
|
+
<div>
|
|
22
|
+
<div>
|
|
23
|
+
<div>
|
|
24
|
+
<div>
|
|
25
|
+
<div><table class="${tableClass}">
|
|
26
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
27
|
+
<tbody>${rows}</tbody>
|
|
28
|
+
</table></div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
${buttonsDiv}
|
|
33
|
+
</div>`;
|
|
34
|
+
} else {
|
|
35
|
+
// table.parentElement.nextElementSibling = buttons div
|
|
36
|
+
document.body.innerHTML = `
|
|
37
|
+
<div>
|
|
38
|
+
<div>
|
|
39
|
+
<table class="${tableClass}">
|
|
40
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
41
|
+
<tbody>${rows}</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
</div>
|
|
44
|
+
${buttonsDiv}
|
|
45
|
+
</div>`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return document.querySelector('table');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function setupDivList({ nbItems = 1, withButtonsDiv = true } = {}) {
|
|
52
|
+
const items = Array.from({ length: nbItems }, (_, i) => `
|
|
53
|
+
<div class="multiple_action" data-action_multiple_input_name="ids[]" data-action_multiple_item_id="${i + 1}">
|
|
54
|
+
Item ${i + 1}
|
|
55
|
+
</div>`).join('');
|
|
56
|
+
|
|
57
|
+
const buttonsDiv = withButtonsDiv
|
|
58
|
+
? `<div class="action_multiple_buttons"><button>Action</button></div>`
|
|
59
|
+
: '';
|
|
60
|
+
|
|
61
|
+
document.body.innerHTML = `
|
|
62
|
+
<div id="content">
|
|
63
|
+
${items}
|
|
64
|
+
</div>
|
|
65
|
+
${buttonsDiv}`;
|
|
66
|
+
|
|
67
|
+
return document.getElementById('content');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── MultipleActionInTable ───────────────────────────────────────────────────
|
|
71
|
+
|
|
6
72
|
describe('MultipleActionInTable', () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
find: jest.fn(() => ({ length: 0 })),
|
|
28
|
-
hasClass: jest.fn(() => false),
|
|
29
|
-
addClass: jest.fn().mockReturnThis(),
|
|
30
|
-
removeClass: jest.fn().mockReturnThis()
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
// Handle element wrapping
|
|
34
|
-
return {
|
|
35
|
-
find: jest.fn(() => ({ length: 0 })),
|
|
36
|
-
hasClass: jest.fn(() => false),
|
|
37
|
-
closest: jest.fn(() => mockTable),
|
|
38
|
-
addClass: jest.fn().mockReturnThis(),
|
|
39
|
-
removeClass: jest.fn().mockReturnThis(),
|
|
40
|
-
data: jest.fn()
|
|
41
|
-
};
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
document.body.innerHTML = '';
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('getDivBtn', () => {
|
|
78
|
+
test('should return button div when found as direct next sibling', () => {
|
|
79
|
+
const table = setupTable();
|
|
80
|
+
const result = MultipleActionInTable.getDivBtn(table);
|
|
81
|
+
expect(result).not.toBeNull();
|
|
82
|
+
expect(result.classList.contains('action_multiple_buttons')).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('should return button div when found in nested structure', () => {
|
|
86
|
+
const table = setupTable({ nested: true });
|
|
87
|
+
const result = MultipleActionInTable.getDivBtn(table);
|
|
88
|
+
expect(result).not.toBeNull();
|
|
89
|
+
expect(result.classList.contains('action_multiple_buttons')).toBe(true);
|
|
42
90
|
});
|
|
43
91
|
|
|
92
|
+
test('should return null when button div not found', () => {
|
|
93
|
+
const table = setupTable({ withButtonsDiv: false });
|
|
94
|
+
const result = MultipleActionInTable.getDivBtn(table);
|
|
95
|
+
expect(result).toBeNull();
|
|
96
|
+
});
|
|
44
97
|
});
|
|
45
98
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
describe('initCols', () => {
|
|
100
|
+
test('should do nothing if table lacks table-action_multiple class', () => {
|
|
101
|
+
const table = setupTable({ tableClass: 'other-class' });
|
|
102
|
+
MultipleActionInTable.initCols(table);
|
|
103
|
+
expect(table.querySelector('th[data-key="select"]')).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('should do nothing if no buttons div found', () => {
|
|
107
|
+
const table = setupTable({ withButtonsDiv: false, tableClass: 'table-action_multiple' });
|
|
108
|
+
MultipleActionInTable.initCols(table);
|
|
109
|
+
expect(table.querySelector('th[data-key="select"]')).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('should add select th to thead', () => {
|
|
113
|
+
const table = setupTable();
|
|
114
|
+
MultipleActionInTable.initCols(table);
|
|
115
|
+
const th = table.querySelector('thead tr th[data-key="select"]');
|
|
116
|
+
expect(th).not.toBeNull();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('should add checkbox td to each tbody row', () => {
|
|
120
|
+
const table = setupTable({ nbRows: 2 });
|
|
121
|
+
MultipleActionInTable.initCols(table);
|
|
122
|
+
const tds = table.querySelectorAll('tbody tr td.select');
|
|
123
|
+
expect(tds.length).toBe(2);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('should set correct name and value on checkboxes', () => {
|
|
127
|
+
const table = setupTable({ nbRows: 1 });
|
|
128
|
+
MultipleActionInTable.initCols(table);
|
|
129
|
+
const cb = table.querySelector('input.action_multiple_checkbox');
|
|
130
|
+
expect(cb.name).toBe('ids[]');
|
|
131
|
+
expect(cb.value).toBe('1');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should be idempotent (no duplicate cols on second call)', () => {
|
|
135
|
+
const table = setupTable({ nbRows: 2 });
|
|
136
|
+
MultipleActionInTable.initCols(table);
|
|
137
|
+
MultipleActionInTable.initCols(table);
|
|
138
|
+
expect(table.querySelectorAll('th[data-key="select"]').length).toBe(1);
|
|
139
|
+
expect(table.querySelectorAll('tbody tr td.select').length).toBe(2);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('should not throw when table has no <thead>', () => {
|
|
143
|
+
document.body.innerHTML = `
|
|
144
|
+
<div>
|
|
145
|
+
<div>
|
|
146
|
+
<table class="table-action_multiple">
|
|
147
|
+
<tbody>
|
|
148
|
+
<tr data-action_multiple_input_name="ids[]" data-action_multiple_item_id="1">
|
|
149
|
+
<td>Item 1</td>
|
|
150
|
+
</tr>
|
|
151
|
+
</tbody>
|
|
152
|
+
</table>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="action_multiple_buttons hide"></div>
|
|
155
|
+
</div>`;
|
|
156
|
+
const table = document.querySelector('table');
|
|
157
|
+
expect(() => MultipleActionInTable.initCols(table)).not.toThrow();
|
|
158
|
+
expect(table.querySelector('th[data-key="select"]')).toBeNull();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('should skip rows with no_items class', () => {
|
|
162
|
+
document.body.innerHTML = `
|
|
163
|
+
<div>
|
|
164
|
+
<div>
|
|
165
|
+
<table class="table-action_multiple">
|
|
166
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
167
|
+
<tbody>
|
|
168
|
+
<tr class="no_items"><td colspan="1">No items</td></tr>
|
|
169
|
+
</tbody>
|
|
170
|
+
</table>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="action_multiple_buttons hide"></div>
|
|
173
|
+
</div>`;
|
|
174
|
+
const table = document.querySelector('table');
|
|
175
|
+
MultipleActionInTable.initCols(table);
|
|
176
|
+
expect(table.querySelector('tbody tr.no_items td.select')).toBeNull();
|
|
177
|
+
});
|
|
49
178
|
});
|
|
50
179
|
|
|
51
|
-
describe('
|
|
52
|
-
test('should
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
180
|
+
describe('init', () => {
|
|
181
|
+
test('should do nothing if table lacks table-action_multiple class', () => {
|
|
182
|
+
const table = setupTable({ tableClass: 'other' });
|
|
183
|
+
MultipleActionInTable.init(table);
|
|
184
|
+
expect(table.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('should do nothing if no buttons div found', () => {
|
|
188
|
+
const table = setupTable({ withButtonsDiv: false });
|
|
189
|
+
MultipleActionInTable.init(table);
|
|
190
|
+
expect(table.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
191
|
+
});
|
|
61
192
|
|
|
62
|
-
|
|
193
|
+
test('should add check-all input to first th', () => {
|
|
194
|
+
const table = setupTable();
|
|
195
|
+
MultipleActionInTable.init(table);
|
|
196
|
+
expect(table.querySelector('thead tr th input.action_multiple_check_all')).not.toBeNull();
|
|
197
|
+
});
|
|
63
198
|
|
|
64
|
-
|
|
65
|
-
|
|
199
|
+
test('should add arrow image to buttons div once', () => {
|
|
200
|
+
const table = setupTable();
|
|
201
|
+
MultipleActionInTable.init(table);
|
|
202
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
203
|
+
expect(divBtn.querySelector('img')).not.toBeNull();
|
|
66
204
|
});
|
|
67
205
|
|
|
68
|
-
test('should
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
mockTable = {
|
|
76
|
-
parent: jest.fn(() => ({
|
|
77
|
-
next: jest.fn(() => notButtonDiv),
|
|
78
|
-
parent: jest.fn(() => ({
|
|
79
|
-
parent: jest.fn(() => ({
|
|
80
|
-
parent: jest.fn(() => ({
|
|
81
|
-
next: jest.fn(() => mockButtonDiv)
|
|
82
|
-
}))
|
|
83
|
-
}))
|
|
84
|
-
}))
|
|
85
|
-
}))
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const result = MultipleActionInTable.getDivBtn(mockTable);
|
|
89
|
-
|
|
90
|
-
expect(result).toBe(mockButtonDiv);
|
|
206
|
+
test('should not add arrow image twice when called again', () => {
|
|
207
|
+
const table = setupTable();
|
|
208
|
+
MultipleActionInTable.init(table);
|
|
209
|
+
MultipleActionInTable.init(table);
|
|
210
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
211
|
+
expect(divBtn.querySelectorAll('img').length).toBe(1);
|
|
91
212
|
});
|
|
92
213
|
|
|
93
|
-
test('
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
mockTable = {
|
|
98
|
-
parent: jest.fn(() => ({
|
|
99
|
-
next: jest.fn(() => notButtonDiv),
|
|
100
|
-
parent: jest.fn(() => ({
|
|
101
|
-
parent: jest.fn(() => ({
|
|
102
|
-
parent: jest.fn(() => ({
|
|
103
|
-
next: jest.fn(() => notButtonDiv)
|
|
104
|
-
}))
|
|
105
|
-
}))
|
|
106
|
-
}))
|
|
107
|
-
}))
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const result = MultipleActionInTable.getDivBtn(mockTable);
|
|
214
|
+
test('check-all toggles all checkboxes to checked when none checked', () => {
|
|
215
|
+
const table = setupTable({ nbRows: 2 });
|
|
216
|
+
MultipleActionInTable.init(table);
|
|
111
217
|
|
|
112
|
-
|
|
218
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
219
|
+
checkAll.click();
|
|
220
|
+
|
|
221
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
222
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(true));
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('check-all unchecks all when all are checked', () => {
|
|
226
|
+
const table = setupTable({ nbRows: 2 });
|
|
227
|
+
MultipleActionInTable.init(table);
|
|
228
|
+
|
|
229
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
230
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
231
|
+
|
|
232
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
233
|
+
checkAll.click();
|
|
234
|
+
|
|
235
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(false));
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('checking a row checkbox updates check-all state', () => {
|
|
239
|
+
const table = setupTable({ nbRows: 2 });
|
|
240
|
+
MultipleActionInTable.init(table);
|
|
241
|
+
|
|
242
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
243
|
+
checkboxes.forEach(cb => {
|
|
244
|
+
cb.checked = true;
|
|
245
|
+
cb.dispatchEvent(new Event('change'));
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
249
|
+
expect(checkAll.checked).toBe(true);
|
|
113
250
|
});
|
|
114
251
|
});
|
|
115
252
|
|
|
116
253
|
describe('updateCheckbox', () => {
|
|
117
|
-
test('should hide
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
MultipleActionInTable.updateCheckbox(mockTable);
|
|
151
|
-
|
|
152
|
-
expect(mockCheckboxSelectAll.addClass).toHaveBeenCalledWith('hide');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('should check select-all checkbox when all checkboxes are checked', () => {
|
|
156
|
-
const mockCheckboxSelectAll = {
|
|
157
|
-
addClass: jest.fn(),
|
|
158
|
-
removeClass: jest.fn(),
|
|
159
|
-
prop: jest.fn()
|
|
160
|
-
};
|
|
161
|
-
const mockButtonDiv = {
|
|
162
|
-
hasClass: jest.fn(() => false)
|
|
163
|
-
};
|
|
164
|
-
mockTable = {
|
|
165
|
-
find: jest.fn((selector) => {
|
|
166
|
-
if (selector === 'input.action_multiple_checkbox') {
|
|
167
|
-
return { length: 3 };
|
|
168
|
-
}
|
|
169
|
-
if (selector === 'input.action_multiple_checkbox:checked') {
|
|
170
|
-
return { length: 3 };
|
|
171
|
-
}
|
|
172
|
-
if (selector === 'thead tr th input.action_multiple_check_all') {
|
|
173
|
-
return mockCheckboxSelectAll;
|
|
174
|
-
}
|
|
175
|
-
return { length: 0 };
|
|
176
|
-
}),
|
|
177
|
-
parent: jest.fn(() => ({
|
|
178
|
-
next: jest.fn(() => mockButtonDiv),
|
|
179
|
-
parent: jest.fn(() => ({
|
|
180
|
-
parent: jest.fn(() => ({
|
|
181
|
-
parent: jest.fn(() => ({
|
|
182
|
-
next: jest.fn(() => mockButtonDiv)
|
|
183
|
-
}))
|
|
184
|
-
}))
|
|
185
|
-
}))
|
|
186
|
-
}))
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
MultipleActionInTable.updateCheckbox(mockTable);
|
|
190
|
-
|
|
191
|
-
expect(mockCheckboxSelectAll.removeClass).toHaveBeenCalledWith('hide');
|
|
192
|
-
expect(mockCheckboxSelectAll.prop).toHaveBeenCalledWith('checked', true);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test('should uncheck select-all checkbox when not all checkboxes are checked', () => {
|
|
196
|
-
const mockCheckboxSelectAll = {
|
|
197
|
-
addClass: jest.fn(),
|
|
198
|
-
removeClass: jest.fn(),
|
|
199
|
-
prop: jest.fn()
|
|
200
|
-
};
|
|
201
|
-
const mockButtonDiv = {
|
|
202
|
-
hasClass: jest.fn(() => false)
|
|
203
|
-
};
|
|
204
|
-
mockTable = {
|
|
205
|
-
find: jest.fn((selector) => {
|
|
206
|
-
if (selector === 'input.action_multiple_checkbox') {
|
|
207
|
-
return { length: 5 };
|
|
208
|
-
}
|
|
209
|
-
if (selector === 'input.action_multiple_checkbox:checked') {
|
|
210
|
-
return { length: 2 };
|
|
211
|
-
}
|
|
212
|
-
if (selector === 'thead tr th input.action_multiple_check_all') {
|
|
213
|
-
return mockCheckboxSelectAll;
|
|
214
|
-
}
|
|
215
|
-
return { length: 0 };
|
|
216
|
-
}),
|
|
217
|
-
parent: jest.fn(() => ({
|
|
218
|
-
next: jest.fn(() => mockButtonDiv),
|
|
219
|
-
parent: jest.fn(() => ({
|
|
220
|
-
parent: jest.fn(() => ({
|
|
221
|
-
parent: jest.fn(() => ({
|
|
222
|
-
next: jest.fn(() => mockButtonDiv)
|
|
223
|
-
}))
|
|
224
|
-
}))
|
|
225
|
-
}))
|
|
226
|
-
}))
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
MultipleActionInTable.updateCheckbox(mockTable);
|
|
230
|
-
|
|
231
|
-
expect(mockCheckboxSelectAll.removeClass).toHaveBeenCalledWith('hide');
|
|
232
|
-
expect(mockCheckboxSelectAll.prop).toHaveBeenCalledWith('checked', false);
|
|
254
|
+
test('should hide check-all when no checkboxes exist', () => {
|
|
255
|
+
document.body.innerHTML = `
|
|
256
|
+
<div>
|
|
257
|
+
<div>
|
|
258
|
+
<table class="table-action_multiple">
|
|
259
|
+
<thead><tr><th><input type="checkbox" class="action_multiple_check_all" /></th></tr></thead>
|
|
260
|
+
<tbody></tbody>
|
|
261
|
+
</table>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="action_multiple_buttons hide"></div>
|
|
264
|
+
</div>`;
|
|
265
|
+
const table = document.querySelector('table');
|
|
266
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
267
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
268
|
+
expect(checkAll.classList.contains('hide')).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('should check check-all when all checkboxes are checked', () => {
|
|
272
|
+
const table = setupTable({ nbRows: 2 });
|
|
273
|
+
MultipleActionInTable.init(table);
|
|
274
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
275
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
276
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
277
|
+
expect(table.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test('should uncheck check-all when not all checkboxes are checked', () => {
|
|
281
|
+
const table = setupTable({ nbRows: 2 });
|
|
282
|
+
MultipleActionInTable.init(table);
|
|
283
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
284
|
+
checkboxes[0].checked = true;
|
|
285
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
286
|
+
expect(table.querySelector('input.action_multiple_check_all').checked).toBe(false);
|
|
233
287
|
});
|
|
234
288
|
});
|
|
235
289
|
|
|
236
290
|
describe('showButtonsAction', () => {
|
|
237
291
|
test('should return early when button div is null', () => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
next: jest.fn(() => ({ hasClass: jest.fn(() => false) })),
|
|
241
|
-
parent: jest.fn(() => ({
|
|
242
|
-
parent: jest.fn(() => ({
|
|
243
|
-
parent: jest.fn(() => ({
|
|
244
|
-
next: jest.fn(() => ({ hasClass: jest.fn(() => false) }))
|
|
245
|
-
}))
|
|
246
|
-
}))
|
|
247
|
-
}))
|
|
248
|
-
})),
|
|
249
|
-
find: jest.fn(() => ({ length: 0 }))
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
expect(() => {
|
|
253
|
-
MultipleActionInTable.showButtonsAction(mockTable);
|
|
254
|
-
}).not.toThrow();
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test('should show button div when items are checked', () => {
|
|
258
|
-
const mockButtonDiv = {
|
|
259
|
-
hasClass: jest.fn(() => true),
|
|
260
|
-
is: jest.fn((selector) => selector === ':hidden'),
|
|
261
|
-
removeClass: jest.fn(),
|
|
262
|
-
addClass: jest.fn(),
|
|
263
|
-
find: jest.fn(() => ({
|
|
264
|
-
length: 1,
|
|
265
|
-
remove: jest.fn(),
|
|
266
|
-
after: jest.fn()
|
|
267
|
-
}))
|
|
268
|
-
};
|
|
269
|
-
mockTable = {
|
|
270
|
-
parent: jest.fn(() => ({
|
|
271
|
-
next: jest.fn(() => mockButtonDiv)
|
|
272
|
-
})),
|
|
273
|
-
find: jest.fn((selector) => {
|
|
274
|
-
if (selector === 'input.action_multiple_checkbox:checked') {
|
|
275
|
-
return { length: 2 };
|
|
276
|
-
}
|
|
277
|
-
return { length: 0 };
|
|
278
|
-
}),
|
|
279
|
-
is: jest.fn(() => false)
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
MultipleActionInTable.showButtonsAction(mockTable);
|
|
283
|
-
|
|
284
|
-
expect(mockButtonDiv.removeClass).toHaveBeenCalledWith('hide');
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test('should hide button div when no items are checked', () => {
|
|
288
|
-
const mockButtonDiv = {
|
|
289
|
-
hasClass: jest.fn(() => true),
|
|
290
|
-
is: jest.fn((selector) => selector === ':visible'),
|
|
291
|
-
removeClass: jest.fn(),
|
|
292
|
-
addClass: jest.fn(),
|
|
293
|
-
find: jest.fn((selector) => {
|
|
294
|
-
if (selector === 'span.no_button') {
|
|
295
|
-
return { remove: jest.fn() };
|
|
296
|
-
}
|
|
297
|
-
if (selector === 'button:visible, a:visible') {
|
|
298
|
-
return { length: 0 };
|
|
299
|
-
}
|
|
300
|
-
if (selector === 'img') {
|
|
301
|
-
return { after: jest.fn() };
|
|
302
|
-
}
|
|
303
|
-
return {
|
|
304
|
-
length: 0,
|
|
305
|
-
remove: jest.fn()
|
|
306
|
-
};
|
|
307
|
-
})
|
|
308
|
-
};
|
|
309
|
-
mockTable = {
|
|
310
|
-
parent: jest.fn(() => ({
|
|
311
|
-
next: jest.fn(() => mockButtonDiv)
|
|
312
|
-
})),
|
|
313
|
-
find: jest.fn(() => ({ length: 0 })),
|
|
314
|
-
is: jest.fn(() => false)
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
MultipleActionInTable.showButtonsAction(mockTable);
|
|
318
|
-
|
|
319
|
-
expect(mockButtonDiv.addClass).toHaveBeenCalledWith('hide');
|
|
292
|
+
const table = setupTable({ withButtonsDiv: false });
|
|
293
|
+
expect(() => MultipleActionInTable.showButtonsAction(table)).not.toThrow();
|
|
320
294
|
});
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
295
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
};
|
|
296
|
+
test('should show buttons div when a checkbox is checked', () => {
|
|
297
|
+
const table = setupTable({ nbRows: 1 });
|
|
298
|
+
MultipleActionInTable.init(table);
|
|
299
|
+
const cb = table.querySelector('input.action_multiple_checkbox');
|
|
300
|
+
cb.checked = true;
|
|
301
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
302
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
303
|
+
expect(divBtn.classList.contains('hide')).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('should hide buttons div when no checkbox is checked', () => {
|
|
307
|
+
const table = setupTable({ nbRows: 1 });
|
|
308
|
+
MultipleActionInTable.init(table);
|
|
309
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
310
|
+
divBtn.classList.remove('hide'); // force visible
|
|
311
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
312
|
+
expect(divBtn.classList.contains('hide')).toBe(true);
|
|
342
313
|
});
|
|
343
314
|
|
|
315
|
+
test('should show "no action" message when div is visible but has no visible buttons', () => {
|
|
316
|
+
document.body.innerHTML = `
|
|
317
|
+
<div>
|
|
318
|
+
<div>
|
|
319
|
+
<table class="table-action_multiple">
|
|
320
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
321
|
+
<tbody>
|
|
322
|
+
<tr data-action_multiple_input_name="ids[]" data-action_multiple_item_id="1">
|
|
323
|
+
<td>Item 1</td>
|
|
324
|
+
</tr>
|
|
325
|
+
</tbody>
|
|
326
|
+
</table>
|
|
327
|
+
</div>
|
|
328
|
+
<div class="action_multiple_buttons">
|
|
329
|
+
<img src="" alt="" />
|
|
330
|
+
</div>
|
|
331
|
+
</div>`;
|
|
332
|
+
const table = document.querySelector('table');
|
|
333
|
+
MultipleActionInTable.init(table);
|
|
334
|
+
const cb = table.querySelector('input.action_multiple_checkbox');
|
|
335
|
+
cb.checked = true;
|
|
336
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
337
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
338
|
+
expect(divBtn.querySelector('span.no_button')).not.toBeNull();
|
|
339
|
+
});
|
|
344
340
|
});
|
|
341
|
+
});
|
|
345
342
|
|
|
343
|
+
// ─── MultipleActionInDivList ─────────────────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
describe('MultipleActionInDivList', () => {
|
|
346
346
|
afterEach(() => {
|
|
347
|
-
|
|
348
|
-
delete global.$;
|
|
347
|
+
document.body.innerHTML = '';
|
|
349
348
|
});
|
|
350
349
|
|
|
351
350
|
describe('getButtonsDiv', () => {
|
|
352
|
-
test('should return buttons div when found', () => {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
};
|
|
351
|
+
test('should return buttons div when found as next sibling', () => {
|
|
352
|
+
const contentDiv = setupDivList();
|
|
353
|
+
const result = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
354
|
+
expect(result).not.toBeNull();
|
|
355
|
+
expect(result.classList.contains('action_multiple_buttons')).toBe(true);
|
|
356
|
+
});
|
|
359
357
|
|
|
360
|
-
|
|
358
|
+
test('should return null when buttons div not found', () => {
|
|
359
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
360
|
+
const result = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
361
|
+
expect(result).toBeNull();
|
|
362
|
+
});
|
|
363
|
+
});
|
|
361
364
|
|
|
362
|
-
|
|
363
|
-
|
|
365
|
+
describe('init', () => {
|
|
366
|
+
test('should return early when no buttons div found', () => {
|
|
367
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
368
|
+
MultipleActionInDivList.init(contentDiv);
|
|
369
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
364
370
|
});
|
|
365
371
|
|
|
366
|
-
test('should return
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
372
|
+
test('should return early when no .multiple_action divs found', () => {
|
|
373
|
+
document.body.innerHTML = `
|
|
374
|
+
<div id="content"></div>
|
|
375
|
+
<div class="action_multiple_buttons"></div>`;
|
|
376
|
+
const contentDiv = document.getElementById('content');
|
|
377
|
+
MultipleActionInDivList.init(contentDiv);
|
|
378
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
379
|
+
});
|
|
373
380
|
|
|
374
|
-
|
|
381
|
+
test('should add checkbox to each .multiple_action div', () => {
|
|
382
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
383
|
+
MultipleActionInDivList.init(contentDiv);
|
|
384
|
+
expect(contentDiv.querySelectorAll('input.action_multiple_checkbox').length).toBe(2);
|
|
385
|
+
});
|
|
375
386
|
|
|
376
|
-
|
|
387
|
+
test('should set correct name and value on checkboxes', () => {
|
|
388
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
389
|
+
MultipleActionInDivList.init(contentDiv);
|
|
390
|
+
const cb = contentDiv.querySelector('input.action_multiple_checkbox');
|
|
391
|
+
expect(cb.name).toBe('ids[]');
|
|
392
|
+
expect(cb.value).toBe('1');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test('should add check-all input', () => {
|
|
396
|
+
const contentDiv = setupDivList();
|
|
397
|
+
MultipleActionInDivList.init(contentDiv);
|
|
398
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).not.toBeNull();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test('should add arrow image to buttons div once', () => {
|
|
402
|
+
const contentDiv = setupDivList();
|
|
403
|
+
MultipleActionInDivList.init(contentDiv);
|
|
404
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
405
|
+
expect(buttonsDiv.querySelector('img')).not.toBeNull();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test('should not add arrow image twice when called again', () => {
|
|
409
|
+
const contentDiv = setupDivList();
|
|
410
|
+
MultipleActionInDivList.init(contentDiv);
|
|
411
|
+
MultipleActionInDivList.init(contentDiv);
|
|
412
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
413
|
+
expect(buttonsDiv.querySelectorAll('img').length).toBe(1);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('check-all toggles all checkboxes to checked when none checked', () => {
|
|
417
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
418
|
+
MultipleActionInDivList.init(contentDiv);
|
|
419
|
+
const checkAll = contentDiv.querySelector('input.action_multiple_check_all');
|
|
420
|
+
checkAll.click();
|
|
421
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
422
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(true));
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('check-all unchecks all when all are checked', () => {
|
|
426
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
427
|
+
MultipleActionInDivList.init(contentDiv);
|
|
428
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
429
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
430
|
+
const checkAll = contentDiv.querySelector('input.action_multiple_check_all');
|
|
431
|
+
checkAll.click();
|
|
432
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(false));
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test('checking a checkbox updates check-all state', () => {
|
|
436
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
437
|
+
MultipleActionInDivList.init(contentDiv);
|
|
438
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
439
|
+
checkboxes.forEach(cb => {
|
|
440
|
+
cb.checked = true;
|
|
441
|
+
cb.dispatchEvent(new Event('change'));
|
|
442
|
+
});
|
|
443
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test('should hide buttons div initially', () => {
|
|
447
|
+
const contentDiv = setupDivList();
|
|
448
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
449
|
+
buttonsDiv.classList.remove('hide');
|
|
450
|
+
MultipleActionInDivList.init(contentDiv);
|
|
451
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(true);
|
|
377
452
|
});
|
|
378
453
|
});
|
|
379
454
|
|
|
380
455
|
describe('updateCheckbox', () => {
|
|
381
|
-
test('should hide
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
MultipleActionInDivList.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
MultipleActionInDivList.updateCheckbox(mockContentDiv);
|
|
433
|
-
|
|
434
|
-
expect(mockCheckboxSelectAll.removeClass).toHaveBeenCalledWith('hide');
|
|
435
|
-
expect(mockCheckboxSelectAll.prop).toHaveBeenCalledWith('checked', true);
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
test('should uncheck select-all checkbox when not all checkboxes are checked', () => {
|
|
439
|
-
const mockCheckboxSelectAll = {
|
|
440
|
-
addClass: jest.fn(),
|
|
441
|
-
removeClass: jest.fn(),
|
|
442
|
-
prop: jest.fn()
|
|
443
|
-
};
|
|
444
|
-
const mockButtonsDiv = {
|
|
445
|
-
hasClass: jest.fn(() => false)
|
|
446
|
-
};
|
|
447
|
-
mockContentDiv = {
|
|
448
|
-
find: jest.fn((selector) => {
|
|
449
|
-
if (selector === 'input.action_multiple_checkbox') {
|
|
450
|
-
return { length: 4 };
|
|
451
|
-
}
|
|
452
|
-
if (selector === 'input.action_multiple_checkbox:checked') {
|
|
453
|
-
return { length: 1 };
|
|
454
|
-
}
|
|
455
|
-
if (selector === 'input.action_multiple_check_all') {
|
|
456
|
-
return mockCheckboxSelectAll;
|
|
457
|
-
}
|
|
458
|
-
return { length: 0 };
|
|
459
|
-
}),
|
|
460
|
-
next: jest.fn(() => mockButtonsDiv)
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
MultipleActionInDivList.updateCheckbox(mockContentDiv);
|
|
464
|
-
|
|
465
|
-
expect(mockCheckboxSelectAll.prop).toHaveBeenCalledWith('checked', false);
|
|
456
|
+
test('should hide check-all when no checkboxes exist', () => {
|
|
457
|
+
document.body.innerHTML = `
|
|
458
|
+
<div id="content">
|
|
459
|
+
<p class="mb-2"><input type="checkbox" class="action_multiple_check_all" /> Tout sélectionner</p>
|
|
460
|
+
</div>
|
|
461
|
+
<div class="action_multiple_buttons hide"></div>`;
|
|
462
|
+
const contentDiv = document.getElementById('content');
|
|
463
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
464
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').classList.contains('hide')).toBe(true);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('should check check-all when all checkboxes are checked', () => {
|
|
468
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
469
|
+
MultipleActionInDivList.init(contentDiv);
|
|
470
|
+
contentDiv.querySelectorAll('input.action_multiple_checkbox').forEach(cb => { cb.checked = true; });
|
|
471
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
472
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('should uncheck check-all when not all checkboxes are checked', () => {
|
|
476
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
477
|
+
MultipleActionInDivList.init(contentDiv);
|
|
478
|
+
contentDiv.querySelectorAll('input.action_multiple_checkbox')[0].checked = true;
|
|
479
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
480
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(false);
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
describe('showButtonsAction', () => {
|
|
485
|
+
test('should return early when buttons div is null', () => {
|
|
486
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
487
|
+
expect(() => MultipleActionInDivList.showButtonsAction(contentDiv)).not.toThrow();
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('should show buttons div when a checkbox is checked', () => {
|
|
491
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
492
|
+
MultipleActionInDivList.init(contentDiv);
|
|
493
|
+
const cb = contentDiv.querySelector('input.action_multiple_checkbox');
|
|
494
|
+
cb.checked = true;
|
|
495
|
+
MultipleActionInDivList.showButtonsAction(contentDiv);
|
|
496
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
497
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(false);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('should hide buttons div when no checkbox is checked', () => {
|
|
501
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
502
|
+
MultipleActionInDivList.init(contentDiv);
|
|
503
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
504
|
+
buttonsDiv.classList.remove('hide');
|
|
505
|
+
MultipleActionInDivList.showButtonsAction(contentDiv);
|
|
506
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(true);
|
|
466
507
|
});
|
|
467
508
|
});
|
|
468
509
|
});
|