@osimatic/helpers-js 1.5.3 → 1.5.4
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 +115 -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 +439 -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,488 @@
|
|
|
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 skip rows with no_items class', () => {
|
|
143
|
+
document.body.innerHTML = `
|
|
144
|
+
<div>
|
|
145
|
+
<div>
|
|
146
|
+
<table class="table-action_multiple">
|
|
147
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
148
|
+
<tbody>
|
|
149
|
+
<tr class="no_items"><td colspan="1">No items</td></tr>
|
|
150
|
+
</tbody>
|
|
151
|
+
</table>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="action_multiple_buttons hide"></div>
|
|
154
|
+
</div>`;
|
|
155
|
+
const table = document.querySelector('table');
|
|
156
|
+
MultipleActionInTable.initCols(table);
|
|
157
|
+
expect(table.querySelector('tbody tr.no_items td.select')).toBeNull();
|
|
158
|
+
});
|
|
49
159
|
});
|
|
50
160
|
|
|
51
|
-
describe('
|
|
52
|
-
test('should
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
parent: jest.fn(() => ({
|
|
58
|
-
next: jest.fn(() => mockButtonDiv)
|
|
59
|
-
}))
|
|
60
|
-
};
|
|
161
|
+
describe('init', () => {
|
|
162
|
+
test('should do nothing if table lacks table-action_multiple class', () => {
|
|
163
|
+
const table = setupTable({ tableClass: 'other' });
|
|
164
|
+
MultipleActionInTable.init(table);
|
|
165
|
+
expect(table.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
166
|
+
});
|
|
61
167
|
|
|
62
|
-
|
|
168
|
+
test('should do nothing if no buttons div found', () => {
|
|
169
|
+
const table = setupTable({ withButtonsDiv: false });
|
|
170
|
+
MultipleActionInTable.init(table);
|
|
171
|
+
expect(table.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
172
|
+
});
|
|
63
173
|
|
|
64
|
-
|
|
65
|
-
|
|
174
|
+
test('should add check-all input to first th', () => {
|
|
175
|
+
const table = setupTable();
|
|
176
|
+
MultipleActionInTable.init(table);
|
|
177
|
+
expect(table.querySelector('thead tr th input.action_multiple_check_all')).not.toBeNull();
|
|
66
178
|
});
|
|
67
179
|
|
|
68
|
-
test('should
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
hasClass: jest.fn(() => false)
|
|
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);
|
|
180
|
+
test('should add arrow image to buttons div once', () => {
|
|
181
|
+
const table = setupTable();
|
|
182
|
+
MultipleActionInTable.init(table);
|
|
183
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
184
|
+
expect(divBtn.querySelector('img')).not.toBeNull();
|
|
91
185
|
});
|
|
92
186
|
|
|
93
|
-
test('should
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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);
|
|
187
|
+
test('should not add arrow image twice when called again', () => {
|
|
188
|
+
const table = setupTable();
|
|
189
|
+
MultipleActionInTable.init(table);
|
|
190
|
+
MultipleActionInTable.init(table);
|
|
191
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
192
|
+
expect(divBtn.querySelectorAll('img').length).toBe(1);
|
|
193
|
+
});
|
|
111
194
|
|
|
112
|
-
|
|
195
|
+
test('check-all toggles all checkboxes to checked when none checked', () => {
|
|
196
|
+
const table = setupTable({ nbRows: 2 });
|
|
197
|
+
MultipleActionInTable.init(table);
|
|
198
|
+
|
|
199
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
200
|
+
checkAll.click();
|
|
201
|
+
|
|
202
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
203
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(true));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('check-all unchecks all when all are checked', () => {
|
|
207
|
+
const table = setupTable({ nbRows: 2 });
|
|
208
|
+
MultipleActionInTable.init(table);
|
|
209
|
+
|
|
210
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
211
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
212
|
+
|
|
213
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
214
|
+
checkAll.click();
|
|
215
|
+
|
|
216
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(false));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('checking a row checkbox updates check-all state', () => {
|
|
220
|
+
const table = setupTable({ nbRows: 2 });
|
|
221
|
+
MultipleActionInTable.init(table);
|
|
222
|
+
|
|
223
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
224
|
+
checkboxes.forEach(cb => {
|
|
225
|
+
cb.checked = true;
|
|
226
|
+
cb.dispatchEvent(new Event('change'));
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
230
|
+
expect(checkAll.checked).toBe(true);
|
|
113
231
|
});
|
|
114
232
|
});
|
|
115
233
|
|
|
116
234
|
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);
|
|
235
|
+
test('should hide check-all when no checkboxes exist', () => {
|
|
236
|
+
document.body.innerHTML = `
|
|
237
|
+
<div>
|
|
238
|
+
<div>
|
|
239
|
+
<table class="table-action_multiple">
|
|
240
|
+
<thead><tr><th><input type="checkbox" class="action_multiple_check_all" /></th></tr></thead>
|
|
241
|
+
<tbody></tbody>
|
|
242
|
+
</table>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="action_multiple_buttons hide"></div>
|
|
245
|
+
</div>`;
|
|
246
|
+
const table = document.querySelector('table');
|
|
247
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
248
|
+
const checkAll = table.querySelector('input.action_multiple_check_all');
|
|
249
|
+
expect(checkAll.classList.contains('hide')).toBe(true);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('should check check-all when all checkboxes are checked', () => {
|
|
253
|
+
const table = setupTable({ nbRows: 2 });
|
|
254
|
+
MultipleActionInTable.init(table);
|
|
255
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
256
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
257
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
258
|
+
expect(table.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('should uncheck check-all when not all checkboxes are checked', () => {
|
|
262
|
+
const table = setupTable({ nbRows: 2 });
|
|
263
|
+
MultipleActionInTable.init(table);
|
|
264
|
+
const checkboxes = table.querySelectorAll('input.action_multiple_checkbox');
|
|
265
|
+
checkboxes[0].checked = true;
|
|
266
|
+
MultipleActionInTable.updateCheckbox(table);
|
|
267
|
+
expect(table.querySelector('input.action_multiple_check_all').checked).toBe(false);
|
|
233
268
|
});
|
|
234
269
|
});
|
|
235
270
|
|
|
236
271
|
describe('showButtonsAction', () => {
|
|
237
272
|
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');
|
|
273
|
+
const table = setupTable({ withButtonsDiv: false });
|
|
274
|
+
expect(() => MultipleActionInTable.showButtonsAction(table)).not.toThrow();
|
|
320
275
|
});
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
276
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
};
|
|
277
|
+
test('should show buttons div when a checkbox is checked', () => {
|
|
278
|
+
const table = setupTable({ nbRows: 1 });
|
|
279
|
+
MultipleActionInTable.init(table);
|
|
280
|
+
const cb = table.querySelector('input.action_multiple_checkbox');
|
|
281
|
+
cb.checked = true;
|
|
282
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
283
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
284
|
+
expect(divBtn.classList.contains('hide')).toBe(false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('should hide buttons div when no checkbox is checked', () => {
|
|
288
|
+
const table = setupTable({ nbRows: 1 });
|
|
289
|
+
MultipleActionInTable.init(table);
|
|
290
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
291
|
+
divBtn.classList.remove('hide'); // force visible
|
|
292
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
293
|
+
expect(divBtn.classList.contains('hide')).toBe(true);
|
|
342
294
|
});
|
|
343
295
|
|
|
296
|
+
test('should show "no action" message when div is visible but has no visible buttons', () => {
|
|
297
|
+
document.body.innerHTML = `
|
|
298
|
+
<div>
|
|
299
|
+
<div>
|
|
300
|
+
<table class="table-action_multiple">
|
|
301
|
+
<thead><tr><th>Name</th></tr></thead>
|
|
302
|
+
<tbody>
|
|
303
|
+
<tr data-action_multiple_input_name="ids[]" data-action_multiple_item_id="1">
|
|
304
|
+
<td>Item 1</td>
|
|
305
|
+
</tr>
|
|
306
|
+
</tbody>
|
|
307
|
+
</table>
|
|
308
|
+
</div>
|
|
309
|
+
<div class="action_multiple_buttons">
|
|
310
|
+
<img src="" alt="" />
|
|
311
|
+
</div>
|
|
312
|
+
</div>`;
|
|
313
|
+
const table = document.querySelector('table');
|
|
314
|
+
MultipleActionInTable.init(table);
|
|
315
|
+
const cb = table.querySelector('input.action_multiple_checkbox');
|
|
316
|
+
cb.checked = true;
|
|
317
|
+
MultipleActionInTable.showButtonsAction(table);
|
|
318
|
+
const divBtn = MultipleActionInTable.getDivBtn(table);
|
|
319
|
+
expect(divBtn.querySelector('span.no_button')).not.toBeNull();
|
|
320
|
+
});
|
|
344
321
|
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// ─── MultipleActionInDivList ─────────────────────────────────────────────────
|
|
345
325
|
|
|
326
|
+
describe('MultipleActionInDivList', () => {
|
|
346
327
|
afterEach(() => {
|
|
347
|
-
|
|
348
|
-
delete global.$;
|
|
328
|
+
document.body.innerHTML = '';
|
|
349
329
|
});
|
|
350
330
|
|
|
351
331
|
describe('getButtonsDiv', () => {
|
|
352
|
-
test('should return buttons div when found', () => {
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
};
|
|
332
|
+
test('should return buttons div when found as next sibling', () => {
|
|
333
|
+
const contentDiv = setupDivList();
|
|
334
|
+
const result = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
335
|
+
expect(result).not.toBeNull();
|
|
336
|
+
expect(result.classList.contains('action_multiple_buttons')).toBe(true);
|
|
337
|
+
});
|
|
359
338
|
|
|
360
|
-
|
|
339
|
+
test('should return null when buttons div not found', () => {
|
|
340
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
341
|
+
const result = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
342
|
+
expect(result).toBeNull();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
361
345
|
|
|
362
|
-
|
|
363
|
-
|
|
346
|
+
describe('init', () => {
|
|
347
|
+
test('should return early when no buttons div found', () => {
|
|
348
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
349
|
+
MultipleActionInDivList.init(contentDiv);
|
|
350
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
364
351
|
});
|
|
365
352
|
|
|
366
|
-
test('should return
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
353
|
+
test('should return early when no .multiple_action divs found', () => {
|
|
354
|
+
document.body.innerHTML = `
|
|
355
|
+
<div id="content"></div>
|
|
356
|
+
<div class="action_multiple_buttons"></div>`;
|
|
357
|
+
const contentDiv = document.getElementById('content');
|
|
358
|
+
MultipleActionInDivList.init(contentDiv);
|
|
359
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).toBeNull();
|
|
360
|
+
});
|
|
373
361
|
|
|
374
|
-
|
|
362
|
+
test('should add checkbox to each .multiple_action div', () => {
|
|
363
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
364
|
+
MultipleActionInDivList.init(contentDiv);
|
|
365
|
+
expect(contentDiv.querySelectorAll('input.action_multiple_checkbox').length).toBe(2);
|
|
366
|
+
});
|
|
375
367
|
|
|
376
|
-
|
|
368
|
+
test('should set correct name and value on checkboxes', () => {
|
|
369
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
370
|
+
MultipleActionInDivList.init(contentDiv);
|
|
371
|
+
const cb = contentDiv.querySelector('input.action_multiple_checkbox');
|
|
372
|
+
expect(cb.name).toBe('ids[]');
|
|
373
|
+
expect(cb.value).toBe('1');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('should add check-all input', () => {
|
|
377
|
+
const contentDiv = setupDivList();
|
|
378
|
+
MultipleActionInDivList.init(contentDiv);
|
|
379
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all')).not.toBeNull();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test('should add arrow image to buttons div once', () => {
|
|
383
|
+
const contentDiv = setupDivList();
|
|
384
|
+
MultipleActionInDivList.init(contentDiv);
|
|
385
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
386
|
+
expect(buttonsDiv.querySelector('img')).not.toBeNull();
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test('should not add arrow image twice when called again', () => {
|
|
390
|
+
const contentDiv = setupDivList();
|
|
391
|
+
MultipleActionInDivList.init(contentDiv);
|
|
392
|
+
MultipleActionInDivList.init(contentDiv);
|
|
393
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
394
|
+
expect(buttonsDiv.querySelectorAll('img').length).toBe(1);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('check-all toggles all checkboxes to checked when none checked', () => {
|
|
398
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
399
|
+
MultipleActionInDivList.init(contentDiv);
|
|
400
|
+
const checkAll = contentDiv.querySelector('input.action_multiple_check_all');
|
|
401
|
+
checkAll.click();
|
|
402
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
403
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(true));
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('check-all unchecks all when all are checked', () => {
|
|
407
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
408
|
+
MultipleActionInDivList.init(contentDiv);
|
|
409
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
410
|
+
checkboxes.forEach(cb => { cb.checked = true; });
|
|
411
|
+
const checkAll = contentDiv.querySelector('input.action_multiple_check_all');
|
|
412
|
+
checkAll.click();
|
|
413
|
+
checkboxes.forEach(cb => expect(cb.checked).toBe(false));
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('checking a checkbox updates check-all state', () => {
|
|
417
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
418
|
+
MultipleActionInDivList.init(contentDiv);
|
|
419
|
+
const checkboxes = contentDiv.querySelectorAll('input.action_multiple_checkbox');
|
|
420
|
+
checkboxes.forEach(cb => {
|
|
421
|
+
cb.checked = true;
|
|
422
|
+
cb.dispatchEvent(new Event('change'));
|
|
423
|
+
});
|
|
424
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('should hide buttons div initially', () => {
|
|
428
|
+
const contentDiv = setupDivList();
|
|
429
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
430
|
+
buttonsDiv.classList.remove('hide');
|
|
431
|
+
MultipleActionInDivList.init(contentDiv);
|
|
432
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(true);
|
|
377
433
|
});
|
|
378
434
|
});
|
|
379
435
|
|
|
380
436
|
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);
|
|
437
|
+
test('should hide check-all when no checkboxes exist', () => {
|
|
438
|
+
document.body.innerHTML = `
|
|
439
|
+
<div id="content">
|
|
440
|
+
<p class="mb-2"><input type="checkbox" class="action_multiple_check_all" /> Tout sélectionner</p>
|
|
441
|
+
</div>
|
|
442
|
+
<div class="action_multiple_buttons hide"></div>`;
|
|
443
|
+
const contentDiv = document.getElementById('content');
|
|
444
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
445
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').classList.contains('hide')).toBe(true);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('should check check-all when all checkboxes are checked', () => {
|
|
449
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
450
|
+
MultipleActionInDivList.init(contentDiv);
|
|
451
|
+
contentDiv.querySelectorAll('input.action_multiple_checkbox').forEach(cb => { cb.checked = true; });
|
|
452
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
453
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(true);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test('should uncheck check-all when not all checkboxes are checked', () => {
|
|
457
|
+
const contentDiv = setupDivList({ nbItems: 2 });
|
|
458
|
+
MultipleActionInDivList.init(contentDiv);
|
|
459
|
+
contentDiv.querySelectorAll('input.action_multiple_checkbox')[0].checked = true;
|
|
460
|
+
MultipleActionInDivList.updateCheckbox(contentDiv);
|
|
461
|
+
expect(contentDiv.querySelector('input.action_multiple_check_all').checked).toBe(false);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
describe('showButtonsAction', () => {
|
|
466
|
+
test('should return early when buttons div is null', () => {
|
|
467
|
+
const contentDiv = setupDivList({ withButtonsDiv: false });
|
|
468
|
+
expect(() => MultipleActionInDivList.showButtonsAction(contentDiv)).not.toThrow();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test('should show buttons div when a checkbox is checked', () => {
|
|
472
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
473
|
+
MultipleActionInDivList.init(contentDiv);
|
|
474
|
+
const cb = contentDiv.querySelector('input.action_multiple_checkbox');
|
|
475
|
+
cb.checked = true;
|
|
476
|
+
MultipleActionInDivList.showButtonsAction(contentDiv);
|
|
477
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
478
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(false);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test('should hide buttons div when no checkbox is checked', () => {
|
|
482
|
+
const contentDiv = setupDivList({ nbItems: 1 });
|
|
483
|
+
MultipleActionInDivList.init(contentDiv);
|
|
484
|
+
const buttonsDiv = MultipleActionInDivList.getButtonsDiv(contentDiv);
|
|
485
|
+
buttonsDiv.classList.remove('hide');
|
|
486
|
+
MultipleActionInDivList.showButtonsAction(contentDiv);
|
|
487
|
+
expect(buttonsDiv.classList.contains('hide')).toBe(true);
|
|
466
488
|
});
|
|
467
489
|
});
|
|
468
490
|
});
|