@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.
Files changed (196) hide show
  1. package/README.md +14 -5
  2. package/dist/ktui.js +1538 -786
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +85 -5
  6. package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
  7. package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
  8. package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
  9. package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
  10. package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
  11. package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
  12. package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
  13. package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
  14. package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
  15. package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
  16. package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
  17. package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
  18. package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
  19. package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
  20. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
  21. package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  22. package/lib/cjs/components/datatable/datatable-layout-plugin.js +338 -0
  23. package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
  24. package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
  25. package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
  26. package/lib/cjs/components/datatable/datatable-local-provider.js +85 -27
  27. package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
  28. package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  29. package/lib/cjs/components/datatable/datatable-pagination-renderer.js +13 -13
  30. package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
  31. package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
  32. package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
  33. package/lib/cjs/components/datatable/datatable-registry.js +66 -0
  34. package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
  35. package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  36. package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
  37. package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
  38. package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
  39. package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
  40. package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
  41. package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
  42. package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
  43. package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
  44. package/lib/cjs/components/datatable/datatable-sort.js +86 -58
  45. package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
  46. package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
  47. package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
  48. package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
  49. package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
  50. package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
  51. package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
  52. package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
  53. package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
  54. package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
  55. package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  56. package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
  57. package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
  58. package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
  59. package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
  60. package/lib/cjs/components/datatable/datatable-utils.js +15 -0
  61. package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
  62. package/lib/cjs/components/datatable/datatable.d.ts +35 -34
  63. package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
  64. package/lib/cjs/components/datatable/datatable.js +233 -497
  65. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  66. package/lib/cjs/components/datatable/index.d.ts +1 -1
  67. package/lib/cjs/components/datatable/index.d.ts.map +1 -1
  68. package/lib/cjs/components/datatable/types.d.ts +127 -11
  69. package/lib/cjs/components/datatable/types.d.ts.map +1 -1
  70. package/lib/cjs/index.d.ts +1 -1
  71. package/lib/cjs/index.d.ts.map +1 -1
  72. package/lib/cjs/index.js +6 -0
  73. package/lib/cjs/index.js.map +1 -1
  74. package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
  75. package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
  76. package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
  77. package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
  78. package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
  79. package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
  80. package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
  81. package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
  82. package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
  83. package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
  84. package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
  85. package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
  86. package/lib/esm/components/datatable/datatable-defaults.js +190 -0
  87. package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
  88. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
  89. package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
  90. package/lib/esm/components/datatable/datatable-layout-plugin.js +334 -0
  91. package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
  92. package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
  93. package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
  94. package/lib/esm/components/datatable/datatable-local-provider.js +85 -27
  95. package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
  96. package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
  97. package/lib/esm/components/datatable/datatable-pagination-renderer.js +13 -13
  98. package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
  99. package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
  100. package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
  101. package/lib/esm/components/datatable/datatable-registry.js +63 -0
  102. package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
  103. package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
  104. package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
  105. package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
  106. package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
  107. package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
  108. package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
  109. package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
  110. package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
  111. package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
  112. package/lib/esm/components/datatable/datatable-sort.js +85 -57
  113. package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
  114. package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
  115. package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
  116. package/lib/esm/components/datatable/datatable-spinner.js +51 -0
  117. package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
  118. package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
  119. package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
  120. package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
  121. package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
  122. package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
  123. package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
  124. package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
  125. package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
  126. package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
  127. package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
  128. package/lib/esm/components/datatable/datatable-utils.js +12 -0
  129. package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
  130. package/lib/esm/components/datatable/datatable.d.ts +35 -34
  131. package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
  132. package/lib/esm/components/datatable/datatable.js +235 -499
  133. package/lib/esm/components/datatable/datatable.js.map +1 -1
  134. package/lib/esm/components/datatable/index.d.ts +1 -1
  135. package/lib/esm/components/datatable/index.d.ts.map +1 -1
  136. package/lib/esm/components/datatable/types.d.ts +127 -11
  137. package/lib/esm/components/datatable/types.d.ts.map +1 -1
  138. package/lib/esm/index.d.ts +1 -1
  139. package/lib/esm/index.d.ts.map +1 -1
  140. package/lib/esm/index.js +6 -0
  141. package/lib/esm/index.js.map +1 -1
  142. package/package.json +5 -1
  143. package/skills/ktui/SKILL.md +711 -0
  144. package/skills/ktui-datatable/SKILL.md +302 -0
  145. package/skills/ktui-install/SKILL.md +150 -0
  146. package/skills/ktui-select/SKILL.md +271 -0
  147. package/src/components/__tests__/component.test.ts +347 -0
  148. package/src/components/collapse/collapse.css +2 -2
  149. package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
  150. package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
  151. package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
  152. package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
  153. package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
  154. package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
  155. package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
  156. package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
  157. package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
  158. package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
  159. package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
  160. package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
  161. package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
  162. package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
  163. package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
  164. package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
  165. package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
  166. package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
  167. package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
  168. package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
  169. package/src/components/datatable/__tests__/pagination-reset.test.ts +147 -6
  170. package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
  171. package/src/components/datatable/__tests__/setup.ts +12 -4
  172. package/src/components/datatable/datatable-checkbox.ts +139 -143
  173. package/src/components/datatable/datatable-column-utils.ts +63 -0
  174. package/src/components/datatable/datatable-contracts.ts +2 -3
  175. package/src/components/datatable/datatable-defaults.ts +204 -0
  176. package/src/components/datatable/datatable-layout-plugin.ts +459 -0
  177. package/src/components/datatable/datatable-local-provider.ts +106 -35
  178. package/src/components/datatable/datatable-pagination-renderer.ts +13 -15
  179. package/src/components/datatable/datatable-registry.ts +89 -0
  180. package/src/components/datatable/datatable-remote-provider.ts +1 -3
  181. package/src/components/datatable/datatable-search-handler.ts +97 -0
  182. package/src/components/datatable/datatable-sort.ts +111 -66
  183. package/src/components/datatable/datatable-spinner.ts +103 -0
  184. package/src/components/datatable/datatable-state-persistence.ts +67 -0
  185. package/src/components/datatable/datatable-table-renderer.ts +81 -18
  186. package/src/components/datatable/datatable-utils.ts +12 -0
  187. package/src/components/datatable/datatable.css +98 -0
  188. package/src/components/datatable/datatable.ts +288 -583
  189. package/src/components/datatable/index.ts +8 -0
  190. package/src/components/datatable/types.ts +157 -23
  191. package/src/helpers/__tests__/dom.test.ts +776 -0
  192. package/src/helpers/__tests__/utils.test.ts +332 -0
  193. package/src/index.ts +15 -0
  194. package/skills/ktui-components/SKILL.md +0 -41
  195. package/skills/ktui-theming/SKILL.md +0 -50
  196. package/src/components/datatable/datatable-event-adapter.ts +0 -21
@@ -0,0 +1,427 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { KTDataTableDomTableRenderer } from '../datatable-table-renderer';
3
+
4
+ function createTableElement() {
5
+ const table = document.createElement('table');
6
+ const thead = table.createTHead();
7
+ const theadRow = document.createElement('tr');
8
+ const th1 = document.createElement('th');
9
+ th1.setAttribute('data-kt-datatable-column', 'name');
10
+ th1.textContent = 'Name';
11
+ const th2 = document.createElement('th');
12
+ th2.setAttribute('data-kt-datatable-column', 'age');
13
+ th2.textContent = 'Age';
14
+ const th3 = document.createElement('th');
15
+ th3.setAttribute('data-kt-datatable-column', 'email');
16
+ th3.textContent = 'Email';
17
+ theadRow.appendChild(th1);
18
+ theadRow.appendChild(th2);
19
+ theadRow.appendChild(th3);
20
+ thead.appendChild(theadRow);
21
+ return { table, thead };
22
+ }
23
+
24
+ function createRendererInput(overrides: Record<string, unknown> = {}) {
25
+ const { table, thead } = createTableElement();
26
+ return {
27
+ config: {
28
+ infoEmpty: 'No records found',
29
+ pageSize: 10,
30
+ ...((overrides.config as object) || {}),
31
+ },
32
+ context: {} as never,
33
+ data: (overrides.data as never[]) || [],
34
+ getLogicalColumnCount:
35
+ (overrides.getLogicalColumnCount as () => number) || (() => 3),
36
+ getState:
37
+ (overrides.getState as () => never) ||
38
+ (() => ({
39
+ page: 1,
40
+ pageSize: 10,
41
+ totalItems: 0,
42
+ totalPages: 0,
43
+ sortField: null,
44
+ sortOrder: '',
45
+ selectedRows: [],
46
+ filters: [],
47
+ search: '',
48
+ originalData: [],
49
+ originalDataAttributes: [],
50
+ })),
51
+ originalClasses: (overrides.originalClasses as never) || {
52
+ tbody: '',
53
+ thead: '',
54
+ tr: [],
55
+ td: [],
56
+ th: [],
57
+ },
58
+ tableElement: (overrides.tableElement as HTMLTableElement) || table,
59
+ theadElement: (overrides.theadElement as HTMLTableSectionElement) || thead,
60
+ ...overrides,
61
+ };
62
+ }
63
+
64
+ const sampleData = [
65
+ { name: 'Alice', age: '30', email: 'alice@example.com' },
66
+ { name: 'Bob', age: '25', email: 'bob@example.com' },
67
+ { name: 'Charlie', age: '35', email: 'charlie@example.com' },
68
+ ];
69
+
70
+ describe('DataTable Fixed Layout', () => {
71
+ let renderer: KTDataTableDomTableRenderer<Record<string, unknown>>;
72
+
73
+ beforeEach(() => {
74
+ document.body.innerHTML = '';
75
+ renderer = new KTDataTableDomTableRenderer();
76
+ });
77
+
78
+ describe('tableLayout config option', () => {
79
+ it('sets table-layout:fixed on table element when tableLayout is "fixed"', () => {
80
+ const input = createRendererInput({
81
+ data: sampleData,
82
+ config: { tableLayout: 'fixed' },
83
+ });
84
+
85
+ renderer.render(input as never);
86
+ expect(input.tableElement.style.tableLayout).toBe('fixed');
87
+ });
88
+
89
+ it('sets table-layout:auto on table element when tableLayout is "auto"', () => {
90
+ const input = createRendererInput({
91
+ data: sampleData,
92
+ config: { tableLayout: 'auto' },
93
+ });
94
+
95
+ renderer.render(input as never);
96
+ expect(input.tableElement.style.tableLayout).toBe('auto');
97
+ });
98
+
99
+ it('defaults to table-layout:auto when tableLayout is not set', () => {
100
+ const input = createRendererInput({
101
+ data: sampleData,
102
+ });
103
+
104
+ renderer.render(input as never);
105
+ expect(input.tableElement.style.tableLayout).toBe('auto');
106
+ });
107
+
108
+ it('sets width:100% on table when tableLayout is "fixed" and no width is set', () => {
109
+ const input = createRendererInput({
110
+ data: sampleData,
111
+ config: { tableLayout: 'fixed' },
112
+ });
113
+
114
+ renderer.render(input as never);
115
+ expect(input.tableElement.style.width).toBe('100%');
116
+ });
117
+
118
+ it('does not overwrite existing width when tableLayout is "fixed"', () => {
119
+ const { table, thead } = createTableElement();
120
+ table.style.width = '80%';
121
+
122
+ const input = createRendererInput({
123
+ data: sampleData,
124
+ config: { tableLayout: 'fixed' },
125
+ tableElement: table,
126
+ theadElement: thead,
127
+ });
128
+
129
+ renderer.render(input as never);
130
+ expect(table.style.width).toBe('80%');
131
+ });
132
+ });
133
+
134
+ describe('colgroup generation', () => {
135
+ it('generates colgroup with correct number of col elements for configured columns', () => {
136
+ const input = createRendererInput({
137
+ data: sampleData,
138
+ config: {
139
+ tableLayout: 'fixed',
140
+ columns: {
141
+ name: {},
142
+ age: {},
143
+ email: {},
144
+ },
145
+ },
146
+ });
147
+
148
+ renderer.render(input as never);
149
+ const colgroup = input.tableElement.querySelector('colgroup');
150
+ expect(colgroup).toBeTruthy();
151
+ expect(colgroup!.children.length).toBe(3);
152
+ expect(colgroup!.children[0].tagName).toBe('COL');
153
+ expect(colgroup!.children[1].tagName).toBe('COL');
154
+ expect(colgroup!.children[2].tagName).toBe('COL');
155
+ });
156
+
157
+ it('generates colgroup for implicit columns from th elements', () => {
158
+ const input = createRendererInput({
159
+ data: sampleData,
160
+ config: { tableLayout: 'fixed' },
161
+ });
162
+
163
+ renderer.render(input as never);
164
+ const colgroup = input.tableElement.querySelector('colgroup');
165
+ expect(colgroup).toBeTruthy();
166
+ expect(colgroup!.children.length).toBe(3);
167
+ });
168
+
169
+ it('does NOT generate colgroup when tableLayout is "auto"', () => {
170
+ const input = createRendererInput({
171
+ data: sampleData,
172
+ config: { tableLayout: 'auto' },
173
+ });
174
+
175
+ renderer.render(input as never);
176
+ const colgroup = input.tableElement.querySelector('colgroup');
177
+ expect(colgroup).toBeNull();
178
+ });
179
+
180
+ it('does NOT generate colgroup when tableLayout is not set (default)', () => {
181
+ const input = createRendererInput({
182
+ data: sampleData,
183
+ });
184
+
185
+ renderer.render(input as never);
186
+ const colgroup = input.tableElement.querySelector('colgroup');
187
+ expect(colgroup).toBeNull();
188
+ });
189
+
190
+ it('inserts colgroup before thead', () => {
191
+ const input = createRendererInput({
192
+ data: sampleData,
193
+ config: { tableLayout: 'fixed' },
194
+ });
195
+
196
+ renderer.render(input as never);
197
+ const children = Array.from(input.tableElement.children);
198
+ const colgroupIndex = children.findIndex(
199
+ (el) => el.tagName === 'COLGROUP',
200
+ );
201
+ const theadIndex = children.findIndex((el) => el.tagName === 'THEAD');
202
+ expect(colgroupIndex).toBeLessThan(theadIndex);
203
+ });
204
+
205
+ it('removes existing colgroup before creating new one on re-render', () => {
206
+ const input = createRendererInput({
207
+ data: sampleData,
208
+ config: { tableLayout: 'fixed' },
209
+ });
210
+
211
+ renderer.render(input as never);
212
+ renderer.render(input as never);
213
+ const colgroups = input.tableElement.querySelectorAll('colgroup');
214
+ expect(colgroups.length).toBe(1);
215
+ });
216
+ });
217
+
218
+ describe('column widths from config.columns', () => {
219
+ it('applies column widths to colgroup col elements', () => {
220
+ const input = createRendererInput({
221
+ data: sampleData,
222
+ config: {
223
+ tableLayout: 'fixed',
224
+ columns: {
225
+ name: { width: '200px' },
226
+ age: { width: '100px' },
227
+ email: { width: '300px' },
228
+ },
229
+ },
230
+ });
231
+
232
+ renderer.render(input as never);
233
+ const cols = input.tableElement.querySelectorAll('colgroup col');
234
+ expect(cols[0].style.width).toBe('200px');
235
+ expect(cols[1].style.width).toBe('100px');
236
+ expect(cols[2].style.width).toBe('300px');
237
+ });
238
+
239
+ it('leaves col width empty when column has no width specified', () => {
240
+ const input = createRendererInput({
241
+ data: sampleData,
242
+ config: {
243
+ tableLayout: 'fixed',
244
+ columns: {
245
+ name: { width: '200px' },
246
+ age: {},
247
+ email: {},
248
+ },
249
+ },
250
+ });
251
+
252
+ renderer.render(input as never);
253
+ const cols = input.tableElement.querySelectorAll('colgroup col');
254
+ expect(cols[0].style.width).toBe('200px');
255
+ expect(cols[1].style.width).toBe('');
256
+ expect(cols[2].style.width).toBe('');
257
+ });
258
+
259
+ it('supports percentage widths', () => {
260
+ const input = createRendererInput({
261
+ data: sampleData,
262
+ config: {
263
+ tableLayout: 'fixed',
264
+ columns: {
265
+ name: { width: '40%' },
266
+ age: { width: '20%' },
267
+ email: { width: '40%' },
268
+ },
269
+ },
270
+ });
271
+
272
+ renderer.render(input as never);
273
+ const cols = input.tableElement.querySelectorAll('colgroup col');
274
+ expect(cols[0].style.width).toBe('40%');
275
+ expect(cols[1].style.width).toBe('20%');
276
+ expect(cols[2].style.width).toBe('40%');
277
+ });
278
+ });
279
+
280
+ describe('implicit column widths from th data attributes', () => {
281
+ it('reads width from data-kt-datatable-column-width attribute on th', () => {
282
+ const table = document.createElement('table');
283
+ const thead = table.createTHead();
284
+ const theadRow = document.createElement('tr');
285
+ const th1 = document.createElement('th');
286
+ th1.setAttribute('data-kt-datatable-column', 'name');
287
+ th1.setAttribute('data-kt-datatable-column-width', '150px');
288
+ th1.textContent = 'Name';
289
+ const th2 = document.createElement('th');
290
+ th2.setAttribute('data-kt-datatable-column', 'age');
291
+ th2.setAttribute('data-kt-datatable-column-width', '80px');
292
+ th2.textContent = 'Age';
293
+ theadRow.appendChild(th1);
294
+ theadRow.appendChild(th2);
295
+ thead.appendChild(theadRow);
296
+
297
+ const input = createRendererInput({
298
+ data: [
299
+ { name: 'Alice', age: '30' },
300
+ { name: 'Bob', age: '25' },
301
+ ],
302
+ config: { tableLayout: 'fixed' },
303
+ tableElement: table,
304
+ theadElement: thead,
305
+ });
306
+
307
+ renderer.render(input as never);
308
+ const cols = table.querySelectorAll('colgroup col');
309
+ expect(cols.length).toBe(2);
310
+ expect(cols[0].style.width).toBe('150px');
311
+ expect(cols[1].style.width).toBe('80px');
312
+ });
313
+
314
+ it('leaves col width empty when th has no data-kt-datatable-column-width', () => {
315
+ const input = createRendererInput({
316
+ data: sampleData,
317
+ config: { tableLayout: 'fixed' },
318
+ });
319
+
320
+ renderer.render(input as never);
321
+ const cols = input.tableElement.querySelectorAll('colgroup col');
322
+ for (const col of Array.from(cols)) {
323
+ expect((col as HTMLTableColElement).style.width).toBe('');
324
+ }
325
+ });
326
+ });
327
+
328
+ describe('colgroup persistence across pagination redraws', () => {
329
+ it('colgroup persists after re-render (simulating page change)', () => {
330
+ const input = createRendererInput({
331
+ data: sampleData,
332
+ config: {
333
+ tableLayout: 'fixed',
334
+ columns: {
335
+ name: { width: '200px' },
336
+ age: { width: '100px' },
337
+ email: { width: '300px' },
338
+ },
339
+ },
340
+ });
341
+
342
+ // Page 1 render
343
+ const tbody1 = renderer.render(input as never);
344
+ input.tableElement.appendChild(tbody1);
345
+
346
+ const colgroup1 = input.tableElement.querySelector('colgroup');
347
+ expect(colgroup1).toBeTruthy();
348
+ expect(colgroup1!.children.length).toBe(3);
349
+
350
+ // Page 2 render (different data)
351
+ input.data = [
352
+ { name: 'Dave', age: '40', email: 'dave@example.com' },
353
+ { name: 'Eve', age: '28', email: 'eve@example.com' },
354
+ ];
355
+ const tbody2 = renderer.render(input as never);
356
+ input.tableElement.appendChild(tbody2);
357
+
358
+ const colgroup2 = input.tableElement.querySelector('colgroup');
359
+ expect(colgroup2).toBeTruthy();
360
+ expect(colgroup2!.children.length).toBe(3);
361
+ expect(
362
+ (colgroup2!.children[0] as HTMLTableColElement).style.width,
363
+ ).toBe('200px');
364
+ expect(
365
+ (colgroup2!.children[1] as HTMLTableColElement).style.width,
366
+ ).toBe('100px');
367
+ expect(
368
+ (colgroup2!.children[2] as HTMLTableColElement).style.width,
369
+ ).toBe('300px');
370
+ });
371
+
372
+ it('table-layout style persists across re-renders', () => {
373
+ const input = createRendererInput({
374
+ data: sampleData,
375
+ config: { tableLayout: 'fixed' },
376
+ });
377
+
378
+ renderer.render(input as never);
379
+ expect(input.tableElement.style.tableLayout).toBe('fixed');
380
+
381
+ input.data = [
382
+ { name: 'Dave', age: '40', email: 'dave@example.com' },
383
+ ];
384
+ renderer.render(input as never);
385
+ expect(input.tableElement.style.tableLayout).toBe('fixed');
386
+ });
387
+ });
388
+
389
+ describe('switching between tableLayout modes', () => {
390
+ it('removes colgroup when switching from fixed to auto', () => {
391
+ const input = createRendererInput({
392
+ data: sampleData,
393
+ config: {
394
+ tableLayout: 'fixed',
395
+ columns: {
396
+ name: { width: '200px' },
397
+ age: { width: '100px' },
398
+ email: { width: '300px' },
399
+ },
400
+ },
401
+ });
402
+
403
+ renderer.render(input as never);
404
+ expect(input.tableElement.querySelector('colgroup')).toBeTruthy();
405
+
406
+ // Switch to auto
407
+ input.config.tableLayout = 'auto';
408
+ renderer.render(input as never);
409
+ expect(input.tableElement.querySelector('colgroup')).toBeNull();
410
+ });
411
+
412
+ it('adds colgroup when switching from auto to fixed', () => {
413
+ const input = createRendererInput({
414
+ data: sampleData,
415
+ config: { tableLayout: 'auto' },
416
+ });
417
+
418
+ renderer.render(input as never);
419
+ expect(input.tableElement.querySelector('colgroup')).toBeNull();
420
+
421
+ // Switch to fixed
422
+ input.config.tableLayout = 'fixed';
423
+ renderer.render(input as never);
424
+ expect(input.tableElement.querySelector('colgroup')).toBeTruthy();
425
+ });
426
+ });
427
+ });