@keenthemes/ktui 1.2.5 → 1.2.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/README.md +14 -5
- package/dist/ktui.js +1538 -786
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +85 -5
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
- package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
- package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +338 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +85 -27
- package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-registry.js +66 -0
- package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +86 -58
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
- package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-utils.js +15 -0
- package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable.d.ts +35 -34
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +233 -497
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/datatable/index.d.ts +1 -1
- package/lib/cjs/components/datatable/index.d.ts.map +1 -1
- package/lib/cjs/components/datatable/types.d.ts +127 -11
- package/lib/cjs/components/datatable/types.d.ts.map +1 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
- package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
- package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-defaults.js +190 -0
- package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js +334 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +85 -27
- package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.js +13 -13
- package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-registry.js +63 -0
- package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
- package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
- package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +85 -57
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-spinner.js +51 -0
- package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-utils.js +12 -0
- package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable.d.ts +35 -34
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +235 -499
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/datatable/index.d.ts +1 -1
- package/lib/esm/components/datatable/index.d.ts.map +1 -1
- package/lib/esm/components/datatable/types.d.ts +127 -11
- package/lib/esm/components/datatable/types.d.ts.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +6 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/ktui/SKILL.md +711 -0
- package/skills/ktui-datatable/SKILL.md +302 -0
- package/skills/ktui-install/SKILL.md +150 -0
- package/skills/ktui-select/SKILL.md +271 -0
- package/src/components/__tests__/component.test.ts +347 -0
- package/src/components/collapse/collapse.css +2 -2
- package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
- package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
- package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
- package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
- package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
- package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
- package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
- package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
- package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
- package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
- package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
- package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
- package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
- package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
- package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
- package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
- package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
- package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
- package/src/components/datatable/__tests__/pagination-reset.test.ts +147 -6
- package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
- package/src/components/datatable/__tests__/setup.ts +12 -4
- package/src/components/datatable/datatable-checkbox.ts +139 -143
- package/src/components/datatable/datatable-column-utils.ts +63 -0
- package/src/components/datatable/datatable-contracts.ts +2 -3
- package/src/components/datatable/datatable-defaults.ts +204 -0
- package/src/components/datatable/datatable-layout-plugin.ts +459 -0
- package/src/components/datatable/datatable-local-provider.ts +106 -35
- package/src/components/datatable/datatable-pagination-renderer.ts +13 -15
- package/src/components/datatable/datatable-registry.ts +89 -0
- package/src/components/datatable/datatable-remote-provider.ts +1 -3
- package/src/components/datatable/datatable-search-handler.ts +97 -0
- package/src/components/datatable/datatable-sort.ts +111 -66
- package/src/components/datatable/datatable-spinner.ts +103 -0
- package/src/components/datatable/datatable-state-persistence.ts +67 -0
- package/src/components/datatable/datatable-table-renderer.ts +81 -18
- package/src/components/datatable/datatable-utils.ts +12 -0
- package/src/components/datatable/datatable.css +98 -0
- package/src/components/datatable/datatable.ts +288 -583
- package/src/components/datatable/index.ts +8 -0
- package/src/components/datatable/types.ts +157 -23
- package/src/helpers/__tests__/dom.test.ts +776 -0
- package/src/helpers/__tests__/utils.test.ts +332 -0
- package/src/index.ts +15 -0
- package/skills/ktui-components/SKILL.md +0 -41
- package/skills/ktui-theming/SKILL.md +0 -50
- package/src/components/datatable/datatable-event-adapter.ts +0 -21
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect, vi } from 'vitest';
|
|
6
6
|
import { KTDataTable } from '../datatable';
|
|
7
|
-
import { createDataTableEventAdapter } from '../datatable-event-adapter';
|
|
8
7
|
import { KTDataTableLocalDataProvider } from '../datatable-local-provider';
|
|
9
8
|
import { KTDataTableDomPaginationRenderer } from '../datatable-pagination-renderer';
|
|
10
9
|
import { KTDataTableRemoteDataProvider } from '../datatable-remote-provider';
|
|
@@ -80,7 +79,12 @@ describe('KTDataTable architecture boundaries', () => {
|
|
|
80
79
|
it('emits through both legacy event channels from one adapter', () => {
|
|
81
80
|
const fireEvent = vi.fn();
|
|
82
81
|
const dispatchEvent = vi.fn();
|
|
83
|
-
const adapter =
|
|
82
|
+
const adapter = {
|
|
83
|
+
emit(eventName: string, eventData?: object): void {
|
|
84
|
+
fireEvent(eventName, eventData);
|
|
85
|
+
dispatchEvent(eventName, eventData);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
84
88
|
|
|
85
89
|
adapter.emit('reload', { page: 1 });
|
|
86
90
|
|
|
@@ -124,6 +128,48 @@ describe('KTDataTable architecture boundaries', () => {
|
|
|
124
128
|
expect(stateStore.getState().originalData).toHaveLength(2);
|
|
125
129
|
});
|
|
126
130
|
|
|
131
|
+
it('keeps programmatic originalData when tbody is empty (custom render demos)', () => {
|
|
132
|
+
const seed = [
|
|
133
|
+
{ id: '1', name: 'Ada' },
|
|
134
|
+
{ id: '2', name: 'Grace' },
|
|
135
|
+
];
|
|
136
|
+
const config = createConfig({
|
|
137
|
+
pageSize: 1,
|
|
138
|
+
_state: {
|
|
139
|
+
originalData: seed,
|
|
140
|
+
originalDataAttributes: [{}, {}],
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const stateStore = new KTDataTableConfigStateStore(config);
|
|
144
|
+
const table = document.createElement('table');
|
|
145
|
+
const thead = table.createTHead();
|
|
146
|
+
thead.innerHTML = `
|
|
147
|
+
<tr>
|
|
148
|
+
<th data-kt-datatable-column="id">ID</th>
|
|
149
|
+
<th data-kt-datatable-column="name">Name</th>
|
|
150
|
+
</tr>
|
|
151
|
+
`;
|
|
152
|
+
const tbody = table.createTBody();
|
|
153
|
+
|
|
154
|
+
const provider = new KTDataTableLocalDataProvider({
|
|
155
|
+
config,
|
|
156
|
+
elements: () => ({
|
|
157
|
+
tableElement: table,
|
|
158
|
+
tbodyElement: tbody,
|
|
159
|
+
theadElement: thead,
|
|
160
|
+
}),
|
|
161
|
+
getLogicalColumnCount: () => 2,
|
|
162
|
+
storeOriginalClasses: vi.fn(),
|
|
163
|
+
stateStore,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const result = provider.fetchSync();
|
|
167
|
+
|
|
168
|
+
expect(result.totalItems).toBe(2);
|
|
169
|
+
expect(result.data).toEqual([{ id: '1', name: 'Ada' }]);
|
|
170
|
+
expect(stateStore.getState().originalData).toHaveLength(2);
|
|
171
|
+
});
|
|
172
|
+
|
|
127
173
|
it('normalizes remote provider fetch results and emits response event', async () => {
|
|
128
174
|
const config = createConfig({ apiEndpoint: '/api/users' });
|
|
129
175
|
const stateStore = new KTDataTableConfigStateStore(config);
|
|
@@ -150,9 +196,7 @@ describe('KTDataTable architecture boundaries', () => {
|
|
|
150
196
|
|
|
151
197
|
expect(result.data).toEqual([{ id: 1, name: 'Ada' }]);
|
|
152
198
|
expect(result.totalItems).toBe(1);
|
|
153
|
-
|
|
154
|
-
response: { data: [{ id: 1, name: 'Ada' }], totalCount: 1 },
|
|
155
|
-
});
|
|
199
|
+
|
|
156
200
|
});
|
|
157
201
|
|
|
158
202
|
it('renders table body output through the table renderer', () => {
|
|
@@ -175,9 +219,13 @@ describe('KTDataTable architecture boundaries', () => {
|
|
|
175
219
|
data: [{ id: '1', name: 'Ada' }],
|
|
176
220
|
getLogicalColumnCount: () => 2,
|
|
177
221
|
getState: () => stateStore.getState(),
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
222
|
+
originalClasses: {
|
|
223
|
+
tbody: 'body-class',
|
|
224
|
+
thead: '',
|
|
225
|
+
tr: ['row-class'],
|
|
226
|
+
td: [['id-cell', 'name-cell']],
|
|
227
|
+
th: [],
|
|
228
|
+
},
|
|
181
229
|
tableElement: table,
|
|
182
230
|
theadElement: thead,
|
|
183
231
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
10
|
-
import {
|
|
10
|
+
import { KTDataTableSortHandler } from '../datatable-sort';
|
|
11
11
|
import { KTDataTableConfigInterface } from '../types';
|
|
12
12
|
|
|
13
13
|
describe('KTDataTable - Currency/numeric sort', () => {
|
|
@@ -29,15 +29,14 @@ describe('KTDataTable - Currency/numeric sort', () => {
|
|
|
29
29
|
price: { sortType: 'numeric' as const },
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
|
-
const handler =
|
|
33
|
-
config as KTDataTableConfigInterface,
|
|
34
|
-
thead,
|
|
35
|
-
() => ({ sortField: null, sortOrder: '' }),
|
|
36
|
-
noop,
|
|
37
|
-
noop,
|
|
38
|
-
noop,
|
|
39
|
-
|
|
40
|
-
);
|
|
32
|
+
const handler = new KTDataTableSortHandler({
|
|
33
|
+
config: config as KTDataTableConfigInterface,
|
|
34
|
+
theadElement: thead,
|
|
35
|
+
getState: () => ({ sortField: null, sortOrder: '' }),
|
|
36
|
+
setState: noop,
|
|
37
|
+
emit: noop,
|
|
38
|
+
updateData: noop,
|
|
39
|
+
});
|
|
41
40
|
|
|
42
41
|
const data = [
|
|
43
42
|
{ price: '£123' },
|
|
@@ -58,15 +57,14 @@ describe('KTDataTable - Currency/numeric sort', () => {
|
|
|
58
57
|
price: { sortType: 'numeric' as const },
|
|
59
58
|
},
|
|
60
59
|
};
|
|
61
|
-
const handler =
|
|
62
|
-
config as KTDataTableConfigInterface,
|
|
63
|
-
thead,
|
|
64
|
-
() => ({ sortField: null, sortOrder: '' }),
|
|
65
|
-
noop,
|
|
66
|
-
noop,
|
|
67
|
-
noop,
|
|
68
|
-
|
|
69
|
-
);
|
|
60
|
+
const handler = new KTDataTableSortHandler({
|
|
61
|
+
config: config as KTDataTableConfigInterface,
|
|
62
|
+
theadElement: thead,
|
|
63
|
+
getState: () => ({ sortField: null, sortOrder: '' }),
|
|
64
|
+
setState: noop,
|
|
65
|
+
emit: noop,
|
|
66
|
+
updateData: noop,
|
|
67
|
+
});
|
|
70
68
|
|
|
71
69
|
const data = [{ price: '£5' }, { price: '£20' }, { price: '£123' }];
|
|
72
70
|
const sorted = handler.sortData(data, 'price', 'desc');
|
|
@@ -79,15 +77,14 @@ describe('KTDataTable - Currency/numeric sort', () => {
|
|
|
79
77
|
|
|
80
78
|
it('without sortType numeric, sorts lexicographically (e.g. £123 before £20)', () => {
|
|
81
79
|
const config = { columns: {} };
|
|
82
|
-
const handler =
|
|
83
|
-
config as KTDataTableConfigInterface,
|
|
84
|
-
thead,
|
|
85
|
-
() => ({ sortField: null, sortOrder: '' }),
|
|
86
|
-
noop,
|
|
87
|
-
noop,
|
|
88
|
-
noop,
|
|
89
|
-
|
|
90
|
-
);
|
|
80
|
+
const handler = new KTDataTableSortHandler({
|
|
81
|
+
config: config as KTDataTableConfigInterface,
|
|
82
|
+
theadElement: thead,
|
|
83
|
+
getState: () => ({ sortField: null, sortOrder: '' }),
|
|
84
|
+
setState: noop,
|
|
85
|
+
emit: noop,
|
|
86
|
+
updateData: noop,
|
|
87
|
+
});
|
|
91
88
|
|
|
92
89
|
const data = [{ price: '£123' }, { price: '£20' }, { price: '£5' }];
|
|
93
90
|
const sorted = handler.sortData(data, 'price', 'asc');
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { KTDataTableCheckboxHandler } from '../datatable-checkbox';
|
|
3
|
+
import type { KTDataTableCheckboxDeps } from '../datatable-checkbox';
|
|
4
|
+
import type { KTDataTableConfigInterface } from '../types';
|
|
5
|
+
|
|
6
|
+
function createCheckboxTable() {
|
|
7
|
+
const container = document.createElement('div');
|
|
8
|
+
container.innerHTML = `
|
|
9
|
+
<table>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr><th><input type="checkbox" data-kt-datatable-check="true" /></th></tr>
|
|
12
|
+
</thead>
|
|
13
|
+
<tbody>
|
|
14
|
+
<tr><td><input type="checkbox" data-kt-datatable-row-check="true" value="1" /></td></tr>
|
|
15
|
+
<tr><td><input type="checkbox" data-kt-datatable-row-check="true" value="2" /></td></tr>
|
|
16
|
+
<tr><td><input type="checkbox" data-kt-datatable-row-check="true" value="3" /></td></tr>
|
|
17
|
+
</tbody>
|
|
18
|
+
</table>
|
|
19
|
+
`;
|
|
20
|
+
document.body.appendChild(container);
|
|
21
|
+
const headerCheck =
|
|
22
|
+
container.querySelector<HTMLInputElement>('[data-kt-datatable-check]')!;
|
|
23
|
+
const rowChecks = container.querySelectorAll<HTMLInputElement>(
|
|
24
|
+
'[data-kt-datatable-row-check]',
|
|
25
|
+
);
|
|
26
|
+
return { container, headerCheck, rowChecks };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createConfig(
|
|
30
|
+
overrides?: Partial<KTDataTableConfigInterface>,
|
|
31
|
+
): KTDataTableConfigInterface {
|
|
32
|
+
return {
|
|
33
|
+
attributes: {
|
|
34
|
+
check: '[data-kt-datatable-check="true"]',
|
|
35
|
+
checkbox: '[data-kt-datatable-row-check="true"]',
|
|
36
|
+
},
|
|
37
|
+
checkbox: { checkedClass: 'checked', preserveSelection: true },
|
|
38
|
+
...overrides,
|
|
39
|
+
} as KTDataTableConfigInterface;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createHandler(
|
|
43
|
+
config?: Partial<KTDataTableConfigInterface>,
|
|
44
|
+
initialSelectedRows: string[] = [],
|
|
45
|
+
) {
|
|
46
|
+
const { container } = createCheckboxTable();
|
|
47
|
+
const cfg = createConfig(config);
|
|
48
|
+
const fireEvent = vi.fn();
|
|
49
|
+
let selectedRows: string[] = [...initialSelectedRows];
|
|
50
|
+
const handler = new KTDataTableCheckboxHandler(container, cfg, fireEvent, {
|
|
51
|
+
getState: () => ({ selectedRows }),
|
|
52
|
+
setSelectedRows: (rows: string[]) => {
|
|
53
|
+
selectedRows = rows;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return { handler, container, fireEvent, getSelectedRows: () => selectedRows };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe('KTDataTableCheckboxHandler', () => {
|
|
60
|
+
describe('Construction and init', () => {
|
|
61
|
+
it('init() queries header and row checkboxes from DOM', () => {
|
|
62
|
+
const { handler, container } = createHandler();
|
|
63
|
+
handler.init();
|
|
64
|
+
const header = container.querySelector<HTMLInputElement>(
|
|
65
|
+
'[data-kt-datatable-check]',
|
|
66
|
+
);
|
|
67
|
+
expect(header).toBeTruthy();
|
|
68
|
+
handler.dispose();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('init() early-returns if config.attributes.check is missing', () => {
|
|
72
|
+
const container = document.createElement('div');
|
|
73
|
+
container.innerHTML = `<table><thead><tr><th><input type="checkbox" /></th></tr></thead><tbody></tbody></table>`;
|
|
74
|
+
document.body.appendChild(container);
|
|
75
|
+
const fireEvent = vi.fn();
|
|
76
|
+
const cfg = {
|
|
77
|
+
attributes: { checkbox: '[data-kt-datatable-row-check="true"]' },
|
|
78
|
+
} as unknown as KTDataTableConfigInterface;
|
|
79
|
+
const handler = new KTDataTableCheckboxHandler(container, cfg, fireEvent, {
|
|
80
|
+
getState: () => ({ selectedRows: [] }),
|
|
81
|
+
setSelectedRows: vi.fn(),
|
|
82
|
+
});
|
|
83
|
+
// Should not throw
|
|
84
|
+
handler.init();
|
|
85
|
+
handler.dispose();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('init() early-returns if header check element not found in DOM', () => {
|
|
89
|
+
const container = document.createElement('div');
|
|
90
|
+
container.innerHTML = `<table><thead><tr><th></th></tr></thead><tbody></tbody></table>`;
|
|
91
|
+
document.body.appendChild(container);
|
|
92
|
+
const fireEvent = vi.fn();
|
|
93
|
+
const cfg = createConfig();
|
|
94
|
+
const handler = new KTDataTableCheckboxHandler(container, cfg, fireEvent, {
|
|
95
|
+
getState: () => ({ selectedRows: [] }),
|
|
96
|
+
setSelectedRows: vi.fn(),
|
|
97
|
+
});
|
|
98
|
+
handler.init();
|
|
99
|
+
// fireEvent should not have been called since header is missing
|
|
100
|
+
expect(fireEvent).not.toHaveBeenCalled();
|
|
101
|
+
handler.dispose();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('init() calls _reapplyCheckedStates and _updateHeaderCheckboxState', () => {
|
|
105
|
+
const { handler, container, getSelectedRows } = createHandler({}, ['1', '3']);
|
|
106
|
+
handler.init();
|
|
107
|
+
// After reapply, row 1 and 3 should be checked
|
|
108
|
+
const rows = container.querySelectorAll<HTMLInputElement>(
|
|
109
|
+
'[data-kt-datatable-row-check]',
|
|
110
|
+
);
|
|
111
|
+
expect(rows[0].checked).toBe(true);
|
|
112
|
+
expect(rows[1].checked).toBe(false);
|
|
113
|
+
expect(rows[2].checked).toBe(true);
|
|
114
|
+
handler.dispose();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('Row checkbox change', () => {
|
|
119
|
+
it('clicking a row checkbox adds value to selectedRows and fires checked + changed', () => {
|
|
120
|
+
const { handler, fireEvent, getSelectedRows } = createHandler();
|
|
121
|
+
handler.init();
|
|
122
|
+
fireEvent.mockClear();
|
|
123
|
+
|
|
124
|
+
const { container } = createCheckboxTable();
|
|
125
|
+
const rowCheck = document.querySelectorAll<HTMLInputElement>(
|
|
126
|
+
'[data-kt-datatable-row-check]',
|
|
127
|
+
)[0];
|
|
128
|
+
|
|
129
|
+
// Trigger change via the handler's init'd DOM
|
|
130
|
+
const allRows = document.querySelectorAll<HTMLInputElement>(
|
|
131
|
+
'[data-kt-datatable-row-check]',
|
|
132
|
+
);
|
|
133
|
+
allRows[0].checked = true;
|
|
134
|
+
allRows[0].dispatchEvent(new Event('input', { bubbles: true }));
|
|
135
|
+
|
|
136
|
+
expect(getSelectedRows()).toContain('1');
|
|
137
|
+
expect(fireEvent).toHaveBeenCalledWith('checked', { value: '1' });
|
|
138
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
139
|
+
handler.dispose();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('unchecking a row removes value from selectedRows and fires unchecked + changed', () => {
|
|
143
|
+
const { handler, fireEvent, getSelectedRows } = createHandler({}, ['1']);
|
|
144
|
+
handler.init();
|
|
145
|
+
fireEvent.mockClear();
|
|
146
|
+
|
|
147
|
+
const allRows = document.querySelectorAll<HTMLInputElement>(
|
|
148
|
+
'[data-kt-datatable-row-check]',
|
|
149
|
+
);
|
|
150
|
+
allRows[0].checked = false;
|
|
151
|
+
allRows[0].dispatchEvent(new Event('input', { bubbles: true }));
|
|
152
|
+
|
|
153
|
+
expect(getSelectedRows()).not.toContain('1');
|
|
154
|
+
expect(fireEvent).toHaveBeenCalledWith('unchecked', { value: '1' });
|
|
155
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
156
|
+
handler.dispose();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('checking an already-checked row does not fire checked again (only changed)', () => {
|
|
160
|
+
const { handler, fireEvent, getSelectedRows } = createHandler({}, ['1']);
|
|
161
|
+
handler.init();
|
|
162
|
+
fireEvent.mockClear();
|
|
163
|
+
|
|
164
|
+
const allRows = document.querySelectorAll<HTMLInputElement>(
|
|
165
|
+
'[data-kt-datatable-row-check]',
|
|
166
|
+
);
|
|
167
|
+
allRows[0].checked = true;
|
|
168
|
+
allRows[0].dispatchEvent(new Event('input', { bubbles: true }));
|
|
169
|
+
|
|
170
|
+
expect(fireEvent).not.toHaveBeenCalledWith('checked', expect.anything());
|
|
171
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
172
|
+
handler.dispose();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('unchecking an already-unchecked row does not fire unchecked again (only changed)', () => {
|
|
176
|
+
const { handler, fireEvent, getSelectedRows } = createHandler();
|
|
177
|
+
handler.init();
|
|
178
|
+
fireEvent.mockClear();
|
|
179
|
+
|
|
180
|
+
const allRows = document.querySelectorAll<HTMLInputElement>(
|
|
181
|
+
'[data-kt-datatable-row-check]',
|
|
182
|
+
);
|
|
183
|
+
allRows[0].checked = false;
|
|
184
|
+
allRows[0].dispatchEvent(new Event('input', { bubbles: true }));
|
|
185
|
+
|
|
186
|
+
expect(fireEvent).not.toHaveBeenCalledWith(
|
|
187
|
+
'unchecked',
|
|
188
|
+
expect.anything(),
|
|
189
|
+
);
|
|
190
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
191
|
+
handler.dispose();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('Header checkbox toggle', () => {
|
|
196
|
+
it('clicking header when none checked → checks all, fires change + checked + changed', () => {
|
|
197
|
+
const { handler, fireEvent, getSelectedRows } = createHandler();
|
|
198
|
+
handler.init();
|
|
199
|
+
fireEvent.mockClear();
|
|
200
|
+
|
|
201
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
202
|
+
'[data-kt-datatable-check]',
|
|
203
|
+
)!;
|
|
204
|
+
header.click();
|
|
205
|
+
|
|
206
|
+
expect(getSelectedRows()).toEqual(['1', '2', '3']);
|
|
207
|
+
expect(fireEvent).toHaveBeenCalledWith('change', { cancel: false });
|
|
208
|
+
expect(fireEvent).toHaveBeenCalledWith('checked');
|
|
209
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
210
|
+
handler.dispose();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('clicking header when all checked → unchecks all, fires change + unchecked + changed', () => {
|
|
214
|
+
const { handler, fireEvent, getSelectedRows } = createHandler({}, ['1', '2', '3']);
|
|
215
|
+
handler.init();
|
|
216
|
+
// Make sure header shows checked
|
|
217
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
218
|
+
'[data-kt-datatable-check]',
|
|
219
|
+
)!;
|
|
220
|
+
header.checked = true;
|
|
221
|
+
fireEvent.mockClear();
|
|
222
|
+
|
|
223
|
+
header.click();
|
|
224
|
+
|
|
225
|
+
expect(getSelectedRows()).toEqual([]);
|
|
226
|
+
expect(fireEvent).toHaveBeenCalledWith('change', { cancel: false });
|
|
227
|
+
expect(fireEvent).toHaveBeenCalledWith('unchecked');
|
|
228
|
+
expect(fireEvent).toHaveBeenCalledWith('changed');
|
|
229
|
+
handler.dispose();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('change event with cancel: true prevents the toggle', () => {
|
|
233
|
+
const { handler, fireEvent, getSelectedRows } = createHandler();
|
|
234
|
+
handler.init();
|
|
235
|
+
// Register a listener that cancels the change
|
|
236
|
+
fireEvent.mockImplementation((eventName: string, data?: any) => {
|
|
237
|
+
if (eventName === 'change' && data) {
|
|
238
|
+
data.cancel = true;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
fireEvent.mockClear();
|
|
242
|
+
|
|
243
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
244
|
+
'[data-kt-datatable-check]',
|
|
245
|
+
)!;
|
|
246
|
+
header.click();
|
|
247
|
+
|
|
248
|
+
// Should not have toggled - rows should remain unchecked
|
|
249
|
+
expect(getSelectedRows()).toEqual([]);
|
|
250
|
+
handler.dispose();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('preserveSelection: false → checking header replaces selectedRows with visible IDs only', () => {
|
|
254
|
+
const { handler, fireEvent, getSelectedRows } = createHandler({
|
|
255
|
+
checkbox: { preserveSelection: false },
|
|
256
|
+
});
|
|
257
|
+
handler.init();
|
|
258
|
+
fireEvent.mockClear();
|
|
259
|
+
|
|
260
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
261
|
+
'[data-kt-datatable-check]',
|
|
262
|
+
)!;
|
|
263
|
+
header.click();
|
|
264
|
+
|
|
265
|
+
expect(getSelectedRows()).toEqual(['1', '2', '3']);
|
|
266
|
+
handler.dispose();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('preserveSelection: false → unchecking header clears selectedRows entirely', () => {
|
|
270
|
+
const { handler, fireEvent, getSelectedRows } = createHandler(
|
|
271
|
+
{ checkbox: { preserveSelection: false } },
|
|
272
|
+
['1', '2', '3', '4'],
|
|
273
|
+
);
|
|
274
|
+
handler.init();
|
|
275
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
276
|
+
'[data-kt-datatable-check]',
|
|
277
|
+
)!;
|
|
278
|
+
header.checked = true;
|
|
279
|
+
fireEvent.mockClear();
|
|
280
|
+
|
|
281
|
+
header.click();
|
|
282
|
+
|
|
283
|
+
expect(getSelectedRows()).toEqual([]);
|
|
284
|
+
handler.dispose();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('preserveSelection: true → checking header merges visible IDs into existing selectedRows', () => {
|
|
288
|
+
const { handler, fireEvent, getSelectedRows } = createHandler(
|
|
289
|
+
{ checkbox: { preserveSelection: true } },
|
|
290
|
+
['99'],
|
|
291
|
+
);
|
|
292
|
+
handler.init();
|
|
293
|
+
fireEvent.mockClear();
|
|
294
|
+
|
|
295
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
296
|
+
'[data-kt-datatable-check]',
|
|
297
|
+
)!;
|
|
298
|
+
header.click();
|
|
299
|
+
|
|
300
|
+
expect(getSelectedRows()).toContain('99');
|
|
301
|
+
expect(getSelectedRows()).toContain('1');
|
|
302
|
+
expect(getSelectedRows()).toContain('2');
|
|
303
|
+
expect(getSelectedRows()).toContain('3');
|
|
304
|
+
handler.dispose();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('preserveSelection: true → unchecking header removes only visible IDs from selectedRows', () => {
|
|
308
|
+
const { handler, fireEvent, getSelectedRows } = createHandler(
|
|
309
|
+
{ checkbox: { preserveSelection: true } },
|
|
310
|
+
['1', '2', '3', '99'],
|
|
311
|
+
);
|
|
312
|
+
handler.init();
|
|
313
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
314
|
+
'[data-kt-datatable-check]',
|
|
315
|
+
)!;
|
|
316
|
+
header.checked = true;
|
|
317
|
+
fireEvent.mockClear();
|
|
318
|
+
|
|
319
|
+
header.click();
|
|
320
|
+
|
|
321
|
+
expect(getSelectedRows()).toEqual(['99']);
|
|
322
|
+
handler.dispose();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('Update header checkbox state', () => {
|
|
327
|
+
it('when 0 rows checked → header.checked = false, header.indeterminate = false', () => {
|
|
328
|
+
const { handler } = createHandler();
|
|
329
|
+
handler.init();
|
|
330
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
331
|
+
'[data-kt-datatable-check]',
|
|
332
|
+
)!;
|
|
333
|
+
expect(header.checked).toBe(false);
|
|
334
|
+
expect(header.indeterminate).toBe(false);
|
|
335
|
+
handler.dispose();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('when some rows checked → header.checked = false, header.indeterminate = true', () => {
|
|
339
|
+
const { handler } = createHandler({}, ['1']);
|
|
340
|
+
handler.init();
|
|
341
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
342
|
+
'[data-kt-datatable-check]',
|
|
343
|
+
)!;
|
|
344
|
+
expect(header.checked).toBe(false);
|
|
345
|
+
expect(header.indeterminate).toBe(true);
|
|
346
|
+
handler.dispose();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('when all rows checked → header.checked = true, header.indeterminate = false', () => {
|
|
350
|
+
const { handler } = createHandler({}, ['1', '2', '3']);
|
|
351
|
+
handler.init();
|
|
352
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
353
|
+
'[data-kt-datatable-check]',
|
|
354
|
+
)!;
|
|
355
|
+
expect(header.checked).toBe(true);
|
|
356
|
+
expect(header.indeterminate).toBe(false);
|
|
357
|
+
handler.dispose();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('Public API', () => {
|
|
362
|
+
it('isChecked() returns headerChecked state', () => {
|
|
363
|
+
const { handler } = createHandler();
|
|
364
|
+
handler.init();
|
|
365
|
+
expect(handler.isChecked()).toBe(false);
|
|
366
|
+
handler.dispose();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('getChecked() returns selectedRows as string[]', () => {
|
|
370
|
+
const { handler } = createHandler({}, ['1', '2']);
|
|
371
|
+
handler.init();
|
|
372
|
+
expect(handler.getChecked()).toEqual(['1', '2']);
|
|
373
|
+
handler.dispose();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('check() calls _change(true) + reapply + updateHeader', () => {
|
|
377
|
+
const { handler, getSelectedRows } = createHandler();
|
|
378
|
+
handler.init();
|
|
379
|
+
handler.check();
|
|
380
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
381
|
+
'[data-kt-datatable-check]',
|
|
382
|
+
)!;
|
|
383
|
+
expect(header.checked).toBe(true);
|
|
384
|
+
expect(getSelectedRows()).toEqual(['1', '2', '3']);
|
|
385
|
+
handler.dispose();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('uncheck() calls _change(false) + reapply + updateHeader', () => {
|
|
389
|
+
const { handler, getSelectedRows } = createHandler({}, ['1', '2', '3']);
|
|
390
|
+
handler.init();
|
|
391
|
+
handler.uncheck();
|
|
392
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
393
|
+
'[data-kt-datatable-check]',
|
|
394
|
+
)!;
|
|
395
|
+
expect(header.checked).toBe(false);
|
|
396
|
+
expect(getSelectedRows()).toEqual([]);
|
|
397
|
+
handler.dispose();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('toggle() calls _checkboxToggle + reapply + updateHeader', () => {
|
|
401
|
+
const { handler, getSelectedRows } = createHandler();
|
|
402
|
+
handler.init();
|
|
403
|
+
handler.toggle();
|
|
404
|
+
expect(getSelectedRows()).toEqual(['1', '2', '3']);
|
|
405
|
+
handler.toggle();
|
|
406
|
+
expect(getSelectedRows()).toEqual([]);
|
|
407
|
+
handler.dispose();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('updateState() re-queries targetElements and reapplies states', () => {
|
|
411
|
+
const { handler, container } = createHandler({}, ['1']);
|
|
412
|
+
handler.init();
|
|
413
|
+
// Add a new row checkbox
|
|
414
|
+
const tbody = container.querySelector('tbody')!;
|
|
415
|
+
const newRow = document.createElement('tr');
|
|
416
|
+
newRow.innerHTML = `<td><input type="checkbox" data-kt-datatable-row-check="true" value="4" /></td>`;
|
|
417
|
+
tbody.appendChild(newRow);
|
|
418
|
+
|
|
419
|
+
handler.updateState();
|
|
420
|
+
// The new row should exist in targetElements now
|
|
421
|
+
const allRows = container.querySelectorAll<HTMLInputElement>(
|
|
422
|
+
'[data-kt-datatable-row-check]',
|
|
423
|
+
);
|
|
424
|
+
expect(allRows.length).toBe(4);
|
|
425
|
+
handler.dispose();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('dispose() removes event listeners and nulls references', () => {
|
|
429
|
+
const { handler, fireEvent } = createHandler();
|
|
430
|
+
handler.init();
|
|
431
|
+
handler.dispose();
|
|
432
|
+
|
|
433
|
+
// After dispose, clicking header should not fire events through handler
|
|
434
|
+
fireEvent.mockClear();
|
|
435
|
+
const header = document.querySelector<HTMLInputElement>(
|
|
436
|
+
'[data-kt-datatable-check]',
|
|
437
|
+
)!;
|
|
438
|
+
header.click();
|
|
439
|
+
// fireEvent from the handler should not be called
|
|
440
|
+
expect(fireEvent).not.toHaveBeenCalled();
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe('Edge cases', () => {
|
|
445
|
+
it('_getSelectedRows() returns [] when state.selectedRows is undefined', () => {
|
|
446
|
+
const container = document.createElement('div');
|
|
447
|
+
container.innerHTML = createCheckboxTable().container.innerHTML;
|
|
448
|
+
document.body.appendChild(container);
|
|
449
|
+
const cfg = createConfig();
|
|
450
|
+
const fireEvent = vi.fn();
|
|
451
|
+
const handler = new KTDataTableCheckboxHandler(container, cfg, fireEvent, {
|
|
452
|
+
getState: () => ({ selectedRows: undefined } as any),
|
|
453
|
+
setSelectedRows: vi.fn(),
|
|
454
|
+
});
|
|
455
|
+
handler.init();
|
|
456
|
+
fireEvent.mockClear();
|
|
457
|
+
// check() triggers _change which calls _getSelectedRows
|
|
458
|
+
handler.check();
|
|
459
|
+
// Should not throw
|
|
460
|
+
expect(true).toBe(true);
|
|
461
|
+
handler.dispose();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('_getVisibleRowIds() returns [] when _targetElements is null', () => {
|
|
465
|
+
const container = document.createElement('div');
|
|
466
|
+
container.innerHTML = `
|
|
467
|
+
<table>
|
|
468
|
+
<thead><tr><th><input type="checkbox" data-kt-datatable-check="true" /></th></tr></thead>
|
|
469
|
+
<tbody></tbody>
|
|
470
|
+
</table>
|
|
471
|
+
`;
|
|
472
|
+
document.body.appendChild(container);
|
|
473
|
+
const cfg = createConfig();
|
|
474
|
+
const fireEvent = vi.fn();
|
|
475
|
+
const handler = new KTDataTableCheckboxHandler(
|
|
476
|
+
container,
|
|
477
|
+
cfg,
|
|
478
|
+
fireEvent,
|
|
479
|
+
{
|
|
480
|
+
getState: () => ({ selectedRows: [] }),
|
|
481
|
+
setSelectedRows: vi.fn(),
|
|
482
|
+
},
|
|
483
|
+
);
|
|
484
|
+
handler.init();
|
|
485
|
+
// No row checkboxes exist, so _targetElements is empty
|
|
486
|
+
// check() should not throw
|
|
487
|
+
handler.check();
|
|
488
|
+
expect(true).toBe(true);
|
|
489
|
+
handler.dispose();
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('init() with checkbox.checkedClass undefined skips class manipulation', () => {
|
|
493
|
+
const container = document.createElement('div');
|
|
494
|
+
container.innerHTML = `
|
|
495
|
+
<table>
|
|
496
|
+
<thead><tr><th><input type="checkbox" data-kt-datatable-check="true" /></th></tr></thead>
|
|
497
|
+
<tbody>
|
|
498
|
+
<tr><td><input type="checkbox" data-kt-datatable-row-check="true" value="1" /></td></tr>
|
|
499
|
+
</tbody>
|
|
500
|
+
</table>
|
|
501
|
+
`;
|
|
502
|
+
document.body.appendChild(container);
|
|
503
|
+
const cfg = {
|
|
504
|
+
attributes: {
|
|
505
|
+
check: '[data-kt-datatable-check="true"]',
|
|
506
|
+
checkbox: '[data-kt-datatable-row-check="true"]',
|
|
507
|
+
},
|
|
508
|
+
checkbox: { preserveSelection: true },
|
|
509
|
+
} as KTDataTableConfigInterface;
|
|
510
|
+
const fireEvent = vi.fn();
|
|
511
|
+
const handler = new KTDataTableCheckboxHandler(
|
|
512
|
+
container,
|
|
513
|
+
cfg,
|
|
514
|
+
fireEvent,
|
|
515
|
+
{
|
|
516
|
+
getState: () => ({ selectedRows: ['1'] }),
|
|
517
|
+
setSelectedRows: vi.fn(),
|
|
518
|
+
},
|
|
519
|
+
);
|
|
520
|
+
// Should not throw even without checkedClass
|
|
521
|
+
handler.init();
|
|
522
|
+
const row = container.querySelector('tr')!;
|
|
523
|
+
expect(row.classList.contains('checked')).toBe(false);
|
|
524
|
+
handler.dispose();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
});
|