@keenthemes/ktui 1.1.0 → 1.1.1

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 (222) hide show
  1. package/README.md +0 -27
  2. package/dist/ktui.js +6790 -14063
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +1132 -2705
  6. package/lib/cjs/components/datatable/__tests__/pagination-reset.test.js +596 -0
  7. package/lib/cjs/components/datatable/__tests__/pagination-reset.test.js.map +1 -0
  8. package/lib/cjs/components/datatable/__tests__/race-conditions.test.js +548 -0
  9. package/lib/cjs/components/datatable/__tests__/race-conditions.test.js.map +1 -0
  10. package/lib/cjs/components/datatable/__tests__/setup.js +63 -0
  11. package/lib/cjs/components/datatable/__tests__/setup.js.map +1 -0
  12. package/lib/cjs/components/datatable/datatable.js +92 -30
  13. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  14. package/lib/cjs/index.js +1 -10
  15. package/lib/cjs/index.js.map +1 -1
  16. package/lib/esm/components/datatable/__tests__/pagination-reset.test.js +594 -0
  17. package/lib/esm/components/datatable/__tests__/pagination-reset.test.js.map +1 -0
  18. package/lib/esm/components/datatable/__tests__/race-conditions.test.js +546 -0
  19. package/lib/esm/components/datatable/__tests__/race-conditions.test.js.map +1 -0
  20. package/lib/esm/components/datatable/__tests__/setup.js +58 -0
  21. package/lib/esm/components/datatable/__tests__/setup.js.map +1 -0
  22. package/lib/esm/components/datatable/datatable.js +92 -30
  23. package/lib/esm/components/datatable/datatable.js.map +1 -1
  24. package/lib/esm/index.js +0 -7
  25. package/lib/esm/index.js.map +1 -1
  26. package/package.json +7 -9
  27. package/src/components/alert/alert.css +188 -429
  28. package/src/components/datatable/__tests__/pagination-reset.test.ts +657 -0
  29. package/src/components/datatable/__tests__/race-conditions.test.ts +455 -0
  30. package/src/components/datatable/__tests__/setup.ts +67 -0
  31. package/src/components/datatable/datatable.ts +66 -11
  32. package/src/components/input/input.css +0 -1
  33. package/src/components/select/select.css +0 -1
  34. package/src/components/select/variants.css +4 -0
  35. package/src/components/textarea/textarea.css +0 -1
  36. package/src/index.ts +0 -10
  37. package/styles.css +0 -1
  38. package/lib/cjs/components/alert/alert.js +0 -1025
  39. package/lib/cjs/components/alert/alert.js.map +0 -1
  40. package/lib/cjs/components/alert/index.js +0 -20
  41. package/lib/cjs/components/alert/index.js.map +0 -1
  42. package/lib/cjs/components/alert/templates.js +0 -120
  43. package/lib/cjs/components/alert/templates.js.map +0 -1
  44. package/lib/cjs/components/alert/types.js +0 -7
  45. package/lib/cjs/components/alert/types.js.map +0 -1
  46. package/lib/cjs/components/datepicker/config/config.js +0 -42
  47. package/lib/cjs/components/datepicker/config/config.js.map +0 -1
  48. package/lib/cjs/components/datepicker/config/index.js +0 -24
  49. package/lib/cjs/components/datepicker/config/index.js.map +0 -1
  50. package/lib/cjs/components/datepicker/config/interfaces.js +0 -7
  51. package/lib/cjs/components/datepicker/config/interfaces.js.map +0 -1
  52. package/lib/cjs/components/datepicker/config/types.js +0 -7
  53. package/lib/cjs/components/datepicker/config/types.js.map +0 -1
  54. package/lib/cjs/components/datepicker/core/event-manager.js +0 -135
  55. package/lib/cjs/components/datepicker/core/event-manager.js.map +0 -1
  56. package/lib/cjs/components/datepicker/core/focus-manager.js +0 -167
  57. package/lib/cjs/components/datepicker/core/focus-manager.js.map +0 -1
  58. package/lib/cjs/components/datepicker/core/helpers.js +0 -219
  59. package/lib/cjs/components/datepicker/core/helpers.js.map +0 -1
  60. package/lib/cjs/components/datepicker/core/index.js +0 -25
  61. package/lib/cjs/components/datepicker/core/index.js.map +0 -1
  62. package/lib/cjs/components/datepicker/core/unified-state-manager.js +0 -394
  63. package/lib/cjs/components/datepicker/core/unified-state-manager.js.map +0 -1
  64. package/lib/cjs/components/datepicker/datepicker.js +0 -2252
  65. package/lib/cjs/components/datepicker/datepicker.js.map +0 -1
  66. package/lib/cjs/components/datepicker/index.js +0 -24
  67. package/lib/cjs/components/datepicker/index.js.map +0 -1
  68. package/lib/cjs/components/datepicker/ui/index.js +0 -23
  69. package/lib/cjs/components/datepicker/ui/index.js.map +0 -1
  70. package/lib/cjs/components/datepicker/ui/input/dropdown.js +0 -489
  71. package/lib/cjs/components/datepicker/ui/input/dropdown.js.map +0 -1
  72. package/lib/cjs/components/datepicker/ui/input/index.js +0 -23
  73. package/lib/cjs/components/datepicker/ui/input/index.js.map +0 -1
  74. package/lib/cjs/components/datepicker/ui/input/segmented-input.js +0 -640
  75. package/lib/cjs/components/datepicker/ui/input/segmented-input.js.map +0 -1
  76. package/lib/cjs/components/datepicker/ui/renderers/calendar.js +0 -446
  77. package/lib/cjs/components/datepicker/ui/renderers/calendar.js.map +0 -1
  78. package/lib/cjs/components/datepicker/ui/renderers/footer.js +0 -42
  79. package/lib/cjs/components/datepicker/ui/renderers/footer.js.map +0 -1
  80. package/lib/cjs/components/datepicker/ui/renderers/header.js +0 -32
  81. package/lib/cjs/components/datepicker/ui/renderers/header.js.map +0 -1
  82. package/lib/cjs/components/datepicker/ui/renderers/index.js +0 -25
  83. package/lib/cjs/components/datepicker/ui/renderers/index.js.map +0 -1
  84. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js +0 -384
  85. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js.map +0 -1
  86. package/lib/cjs/components/datepicker/ui/templates/index.js +0 -22
  87. package/lib/cjs/components/datepicker/ui/templates/index.js.map +0 -1
  88. package/lib/cjs/components/datepicker/ui/templates/templates.js +0 -253
  89. package/lib/cjs/components/datepicker/ui/templates/templates.js.map +0 -1
  90. package/lib/cjs/components/datepicker/utils/date-formatters.js +0 -88
  91. package/lib/cjs/components/datepicker/utils/date-formatters.js.map +0 -1
  92. package/lib/cjs/components/datepicker/utils/date-utils.js +0 -194
  93. package/lib/cjs/components/datepicker/utils/date-utils.js.map +0 -1
  94. package/lib/cjs/components/datepicker/utils/index.js +0 -24
  95. package/lib/cjs/components/datepicker/utils/index.js.map +0 -1
  96. package/lib/cjs/components/datepicker/utils/time-utils.js +0 -213
  97. package/lib/cjs/components/datepicker/utils/time-utils.js.map +0 -1
  98. package/lib/esm/components/alert/alert.js +0 -1022
  99. package/lib/esm/components/alert/alert.js.map +0 -1
  100. package/lib/esm/components/alert/index.js +0 -4
  101. package/lib/esm/components/alert/index.js.map +0 -1
  102. package/lib/esm/components/alert/templates.js +0 -112
  103. package/lib/esm/components/alert/templates.js.map +0 -1
  104. package/lib/esm/components/alert/types.js +0 -6
  105. package/lib/esm/components/alert/types.js.map +0 -1
  106. package/lib/esm/components/datepicker/config/config.js +0 -39
  107. package/lib/esm/components/datepicker/config/config.js.map +0 -1
  108. package/lib/esm/components/datepicker/config/index.js +0 -8
  109. package/lib/esm/components/datepicker/config/index.js.map +0 -1
  110. package/lib/esm/components/datepicker/config/interfaces.js +0 -6
  111. package/lib/esm/components/datepicker/config/interfaces.js.map +0 -1
  112. package/lib/esm/components/datepicker/config/types.js +0 -6
  113. package/lib/esm/components/datepicker/config/types.js.map +0 -1
  114. package/lib/esm/components/datepicker/core/event-manager.js +0 -133
  115. package/lib/esm/components/datepicker/core/event-manager.js.map +0 -1
  116. package/lib/esm/components/datepicker/core/focus-manager.js +0 -164
  117. package/lib/esm/components/datepicker/core/focus-manager.js.map +0 -1
  118. package/lib/esm/components/datepicker/core/helpers.js +0 -211
  119. package/lib/esm/components/datepicker/core/helpers.js.map +0 -1
  120. package/lib/esm/components/datepicker/core/index.js +0 -9
  121. package/lib/esm/components/datepicker/core/index.js.map +0 -1
  122. package/lib/esm/components/datepicker/core/unified-state-manager.js +0 -391
  123. package/lib/esm/components/datepicker/core/unified-state-manager.js.map +0 -1
  124. package/lib/esm/components/datepicker/datepicker.js +0 -2248
  125. package/lib/esm/components/datepicker/datepicker.js.map +0 -1
  126. package/lib/esm/components/datepicker/index.js +0 -7
  127. package/lib/esm/components/datepicker/index.js.map +0 -1
  128. package/lib/esm/components/datepicker/ui/index.js +0 -7
  129. package/lib/esm/components/datepicker/ui/index.js.map +0 -1
  130. package/lib/esm/components/datepicker/ui/input/dropdown.js +0 -486
  131. package/lib/esm/components/datepicker/ui/input/dropdown.js.map +0 -1
  132. package/lib/esm/components/datepicker/ui/input/index.js +0 -7
  133. package/lib/esm/components/datepicker/ui/input/index.js.map +0 -1
  134. package/lib/esm/components/datepicker/ui/input/segmented-input.js +0 -637
  135. package/lib/esm/components/datepicker/ui/input/segmented-input.js.map +0 -1
  136. package/lib/esm/components/datepicker/ui/renderers/calendar.js +0 -443
  137. package/lib/esm/components/datepicker/ui/renderers/calendar.js.map +0 -1
  138. package/lib/esm/components/datepicker/ui/renderers/footer.js +0 -39
  139. package/lib/esm/components/datepicker/ui/renderers/footer.js.map +0 -1
  140. package/lib/esm/components/datepicker/ui/renderers/header.js +0 -29
  141. package/lib/esm/components/datepicker/ui/renderers/header.js.map +0 -1
  142. package/lib/esm/components/datepicker/ui/renderers/index.js +0 -9
  143. package/lib/esm/components/datepicker/ui/renderers/index.js.map +0 -1
  144. package/lib/esm/components/datepicker/ui/renderers/time-picker.js +0 -381
  145. package/lib/esm/components/datepicker/ui/renderers/time-picker.js.map +0 -1
  146. package/lib/esm/components/datepicker/ui/templates/index.js +0 -6
  147. package/lib/esm/components/datepicker/ui/templates/index.js.map +0 -1
  148. package/lib/esm/components/datepicker/ui/templates/templates.js +0 -242
  149. package/lib/esm/components/datepicker/ui/templates/templates.js.map +0 -1
  150. package/lib/esm/components/datepicker/utils/date-formatters.js +0 -83
  151. package/lib/esm/components/datepicker/utils/date-formatters.js.map +0 -1
  152. package/lib/esm/components/datepicker/utils/date-utils.js +0 -184
  153. package/lib/esm/components/datepicker/utils/date-utils.js.map +0 -1
  154. package/lib/esm/components/datepicker/utils/index.js +0 -8
  155. package/lib/esm/components/datepicker/utils/index.js.map +0 -1
  156. package/lib/esm/components/datepicker/utils/time-utils.js +0 -201
  157. package/lib/esm/components/datepicker/utils/time-utils.js.map +0 -1
  158. package/src/components/alert/alert.ts +0 -990
  159. package/src/components/alert/index.ts +0 -4
  160. package/src/components/alert/templates.ts +0 -110
  161. package/src/components/alert/tests/accessibility/aria-roles.test.ts +0 -19
  162. package/src/components/alert/tests/accessibility/focus-management.test.ts +0 -19
  163. package/src/components/alert/tests/accessibility/keyboard-nav.test.ts +0 -22
  164. package/src/components/alert/tests/actions/confirm-cancel.test.ts +0 -122
  165. package/src/components/alert/tests/actions/input-field.test.ts +0 -180
  166. package/src/components/alert/tests/alert.basic.test.ts +0 -126
  167. package/src/components/alert/tests/alert.config.test.ts +0 -75
  168. package/src/components/alert/tests/alert.templates.test.ts +0 -17
  169. package/src/components/alert/tests/config/attribute-config.test.ts +0 -94
  170. package/src/components/alert/tests/config/json-config.test.ts +0 -119
  171. package/src/components/alert/tests/config/merging.test.ts +0 -89
  172. package/src/components/alert/tests/dismissal/auto-dismiss.test.ts +0 -96
  173. package/src/components/alert/tests/dismissal/escape-key-dismiss.test.ts +0 -105
  174. package/src/components/alert/tests/dismissal/manual-dismiss.test.ts +0 -90
  175. package/src/components/alert/tests/dismissal/outside-click-dismiss.test.ts +0 -91
  176. package/src/components/alert/tests/edge-cases/invalid-config.test.ts +0 -19
  177. package/src/components/alert/tests/edge-cases/multiple-alerts.test.ts +0 -19
  178. package/src/components/alert/tests/rendering/custom-content.test.ts +0 -81
  179. package/src/components/alert/tests/rendering/info-alert.test.ts +0 -84
  180. package/src/components/alert/tests/rendering/success-alert.test.ts +0 -100
  181. package/src/components/alert/tests/templates/default-templates.test.ts +0 -16
  182. package/src/components/alert/tests/templates/user-templates.test.ts +0 -16
  183. package/src/components/alert/types.ts +0 -145
  184. package/src/components/datepicker/__tests__/datepicker-events.test.ts +0 -356
  185. package/src/components/datepicker/__tests__/datepicker-init.test.ts +0 -343
  186. package/src/components/datepicker/__tests__/datepicker-integration.test.ts +0 -435
  187. package/src/components/datepicker/__tests__/datepicker-timezone.test.ts +0 -220
  188. package/src/components/datepicker/__tests__/segmented-input-focus.test.ts +0 -380
  189. package/src/components/datepicker/__tests__/selective-state-updates.test.ts +0 -400
  190. package/src/components/datepicker/__tests__/state-manager.test.ts +0 -421
  191. package/src/components/datepicker/__tests__/time-preservation.test.ts +0 -387
  192. package/src/components/datepicker/config/config.ts +0 -40
  193. package/src/components/datepicker/config/index.ts +0 -8
  194. package/src/components/datepicker/config/interfaces.ts +0 -82
  195. package/src/components/datepicker/config/types.ts +0 -188
  196. package/src/components/datepicker/core/event-manager.ts +0 -159
  197. package/src/components/datepicker/core/focus-manager.ts +0 -201
  198. package/src/components/datepicker/core/helpers.ts +0 -231
  199. package/src/components/datepicker/core/index.ts +0 -9
  200. package/src/components/datepicker/core/unified-state-manager.ts +0 -459
  201. package/src/components/datepicker/datepicker.css +0 -435
  202. package/src/components/datepicker/datepicker.ts +0 -2548
  203. package/src/components/datepicker/index.ts +0 -8
  204. package/src/components/datepicker/ui/index.ts +0 -7
  205. package/src/components/datepicker/ui/input/dropdown.ts +0 -552
  206. package/src/components/datepicker/ui/input/index.ts +0 -7
  207. package/src/components/datepicker/ui/input/segmented-input.ts +0 -638
  208. package/src/components/datepicker/ui/renderers/__tests__/calendar-optimizations.test.ts +0 -611
  209. package/src/components/datepicker/ui/renderers/calendar.ts +0 -530
  210. package/src/components/datepicker/ui/renderers/footer.ts +0 -43
  211. package/src/components/datepicker/ui/renderers/header.ts +0 -33
  212. package/src/components/datepicker/ui/renderers/index.ts +0 -9
  213. package/src/components/datepicker/ui/renderers/time-picker.ts +0 -438
  214. package/src/components/datepicker/ui/templates/index.ts +0 -6
  215. package/src/components/datepicker/ui/templates/templates.ts +0 -306
  216. package/src/components/datepicker/utils/__tests__/date-formatters.test.ts +0 -160
  217. package/src/components/datepicker/utils/__tests__/date-utils-keys.test.ts +0 -86
  218. package/src/components/datepicker/utils/__tests__/date-utils-timezone.test.ts +0 -215
  219. package/src/components/datepicker/utils/date-formatters.ts +0 -85
  220. package/src/components/datepicker/utils/date-utils.ts +0 -172
  221. package/src/components/datepicker/utils/index.ts +0 -8
  222. package/src/components/datepicker/utils/time-utils.ts +0 -221
@@ -1,611 +0,0 @@
1
- /**
2
- * calendar-optimizations.test.ts - Tests for calendar rendering optimizations
3
- * Tests day name caching, event delegation, cell reference caching, and date key optimizations
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import { renderCalendar } from '../calendar';
8
- import { getDateKey } from '../../../utils/date-utils';
9
- import { defaultTemplates } from '../../templates/templates';
10
-
11
- describe('Calendar Rendering Optimizations', () => {
12
- let container: HTMLElement;
13
-
14
- beforeEach(() => {
15
- container = document.createElement('div');
16
- document.body.appendChild(container);
17
- });
18
-
19
- afterEach(() => {
20
- if (container.parentNode) {
21
- document.body.removeChild(container);
22
- }
23
- });
24
-
25
- // Helper to generate calendar days for a month
26
- function getCalendarDays(year: number, month: number): Date[] {
27
- const firstDay = new Date(year, month, 1);
28
- const lastDay = new Date(year, month + 1, 0);
29
- const days: Date[] = [];
30
- let start = new Date(firstDay);
31
- start.setDate(firstDay.getDate() - firstDay.getDay());
32
- let end = new Date(lastDay);
33
- end.setDate(lastDay.getDate() + (6 - lastDay.getDay()));
34
- for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
35
- days.push(new Date(d));
36
- }
37
- return days;
38
- }
39
-
40
- describe('Day Name Caching', () => {
41
- it('should cache day names per locale', () => {
42
- const days = getCalendarDays(2024, 0);
43
- const onDayClick = vi.fn();
44
- const currentDate = new Date(2024, 0, 15);
45
-
46
- // First render with en-US
47
- const calendar1 = renderCalendar(
48
- defaultTemplates.dayCell as string,
49
- days,
50
- currentDate,
51
- null,
52
- onDayClick,
53
- 'en-US'
54
- );
55
-
56
- // Second render with same locale - should use cache
57
- const calendar2 = renderCalendar(
58
- defaultTemplates.dayCell as string,
59
- days,
60
- currentDate,
61
- null,
62
- onDayClick,
63
- 'en-US'
64
- );
65
-
66
- // Both should have same day names in thead
67
- const dayNames1 = Array.from(calendar1.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
68
- const dayNames2 = Array.from(calendar2.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
69
- expect(dayNames1.length).toBeGreaterThan(0);
70
- expect(dayNames1).toEqual(dayNames2);
71
- });
72
-
73
- it('should cache different locales separately', () => {
74
- const days = getCalendarDays(2024, 0);
75
- const onDayClick = vi.fn();
76
- const currentDate = new Date(2024, 0, 15);
77
-
78
- const calendarEN = renderCalendar(
79
- defaultTemplates.dayCell as string,
80
- days,
81
- currentDate,
82
- null,
83
- onDayClick,
84
- 'en-US'
85
- );
86
-
87
- const calendarDE = renderCalendar(
88
- defaultTemplates.dayCell as string,
89
- days,
90
- currentDate,
91
- null,
92
- onDayClick,
93
- 'de-DE'
94
- );
95
-
96
- // Day names should be different for different locales
97
- const dayNamesEN = Array.from(calendarEN.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
98
- const dayNamesDE = Array.from(calendarDE.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
99
- expect(dayNamesEN.length).toBeGreaterThan(0);
100
- expect(dayNamesDE.length).toBeGreaterThan(0);
101
- // Note: In some test environments, locale formatting might be the same
102
- // So we just verify both have day names
103
- });
104
-
105
- it('should work with multiple locales (en-US, de-DE, fr-FR)', () => {
106
- const days = getCalendarDays(2024, 0);
107
- const onDayClick = vi.fn();
108
- const currentDate = new Date(2024, 0, 15);
109
-
110
- // Render calendars for all three locales
111
- const calendarEN = renderCalendar(
112
- defaultTemplates.dayCell as string,
113
- days,
114
- currentDate,
115
- null,
116
- onDayClick,
117
- 'en-US'
118
- );
119
-
120
- const calendarDE = renderCalendar(
121
- defaultTemplates.dayCell as string,
122
- days,
123
- currentDate,
124
- null,
125
- onDayClick,
126
- 'de-DE'
127
- );
128
-
129
- const calendarFR = renderCalendar(
130
- defaultTemplates.dayCell as string,
131
- days,
132
- currentDate,
133
- null,
134
- onDayClick,
135
- 'fr-FR'
136
- );
137
-
138
- // All should have day names
139
- const dayNamesEN = Array.from(calendarEN.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
140
- const dayNamesDE = Array.from(calendarDE.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
141
- const dayNamesFR = Array.from(calendarFR.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
142
-
143
- expect(dayNamesEN.length).toBe(7);
144
- expect(dayNamesDE.length).toBe(7);
145
- expect(dayNamesFR.length).toBe(7);
146
-
147
- // Verify cache persists across multiple renders of same locale
148
- const calendarEN2 = renderCalendar(
149
- defaultTemplates.dayCell as string,
150
- days,
151
- currentDate,
152
- null,
153
- onDayClick,
154
- 'en-US'
155
- );
156
- const dayNamesEN2 = Array.from(calendarEN2.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
157
- expect(dayNamesEN).toEqual(dayNamesEN2); // Should be cached
158
- });
159
-
160
- it('should work correctly across multiple datepicker instances', () => {
161
- const days = getCalendarDays(2024, 0);
162
- const onDayClick = vi.fn();
163
- const currentDate = new Date(2024, 0, 15);
164
-
165
- // Simulate multiple instances using same locale
166
- const instance1_calendar1 = renderCalendar(
167
- defaultTemplates.dayCell as string,
168
- days,
169
- currentDate,
170
- null,
171
- onDayClick,
172
- 'en-US'
173
- );
174
-
175
- const instance2_calendar1 = renderCalendar(
176
- defaultTemplates.dayCell as string,
177
- days,
178
- currentDate,
179
- null,
180
- onDayClick,
181
- 'en-US'
182
- );
183
-
184
- // Both instances should use the same cached day names
185
- const dayNames1 = Array.from(instance1_calendar1.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
186
- const dayNames2 = Array.from(instance2_calendar1.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
187
-
188
- expect(dayNames1.length).toBe(7);
189
- expect(dayNames2.length).toBe(7);
190
- expect(dayNames1).toEqual(dayNames2); // Cache should be shared across instances
191
-
192
- // Render again with different months - cache should still work
193
- const days2 = getCalendarDays(2024, 1);
194
- const instance1_calendar2 = renderCalendar(
195
- defaultTemplates.dayCell as string,
196
- days2,
197
- new Date(2024, 1, 15),
198
- null,
199
- onDayClick,
200
- 'en-US'
201
- );
202
-
203
- const dayNames1_month2 = Array.from(instance1_calendar2.querySelectorAll('thead th')).map(th => th.textContent?.trim()).filter(Boolean);
204
- expect(dayNames1_month2).toEqual(dayNames1); // Same locale = same cached day names
205
- });
206
- });
207
-
208
- describe('Event Delegation', () => {
209
- it('should use event delegation for click events', () => {
210
- const days = getCalendarDays(2024, 0);
211
- const onDayClick = vi.fn();
212
- const currentDate = new Date(2024, 0, 15);
213
-
214
- const calendar = renderCalendar(
215
- defaultTemplates.dayCell as string,
216
- days,
217
- currentDate,
218
- null,
219
- onDayClick
220
- );
221
-
222
- // Count event listeners - should be minimal (delegated)
223
- const buttons = calendar.querySelectorAll('button[data-day]');
224
- expect(buttons.length).toBeGreaterThan(0);
225
-
226
- // Click a button - should trigger delegated handler
227
- const firstButton = buttons[0] as HTMLButtonElement;
228
- firstButton.click();
229
-
230
- // Should have called onDayClick if date is in current month
231
- // Note: onDayClick only fires for dates in current month
232
- const firstCell = firstButton.closest('td[data-kt-datepicker-day]') as HTMLElement;
233
- const dateAttr = firstCell?.getAttribute('data-date');
234
- if (dateAttr) {
235
- const [year, month] = dateAttr.split('-').map(Number);
236
- if (month - 1 === currentDate.getMonth()) {
237
- expect(onDayClick).toHaveBeenCalled();
238
- }
239
- }
240
- });
241
-
242
- it('should handle hover events via delegation', () => {
243
- const days = getCalendarDays(2024, 0);
244
- const onDayClick = vi.fn();
245
- const currentDate = new Date(2024, 0, 15);
246
-
247
- const calendar = renderCalendar(
248
- defaultTemplates.dayCell as string,
249
- days,
250
- currentDate,
251
- null,
252
- onDayClick
253
- );
254
-
255
- const firstCell = calendar.querySelector('td[data-kt-datepicker-day]') as HTMLElement;
256
- const firstButton = firstCell?.querySelector('button[data-day]') as HTMLButtonElement;
257
-
258
- if (firstButton) {
259
- // Simulate mouseover - should trigger delegated handler
260
- const mouseoverEvent = new MouseEvent('mouseover', { bubbles: true });
261
- firstButton.dispatchEvent(mouseoverEvent);
262
-
263
- // Cell should have hover attribute set by delegated handler
264
- expect(firstCell.hasAttribute('data-kt-hover')).toBe(true);
265
- }
266
- });
267
-
268
- it('should only attach listeners to calendar table, not individual cells', () => {
269
- const days = getCalendarDays(2024, 0);
270
- const onDayClick = vi.fn();
271
- const currentDate = new Date(2024, 0, 15);
272
-
273
- const calendar = renderCalendar(
274
- defaultTemplates.dayCell as string,
275
- days,
276
- currentDate,
277
- null,
278
- onDayClick
279
- );
280
-
281
- // Verify calendar has event listeners (delegated)
282
- // We can't directly count listeners, but we can verify behavior
283
- const buttons = calendar.querySelectorAll('button[data-day]');
284
- expect(buttons.length).toBeGreaterThan(0);
285
-
286
- // All buttons should be clickable via delegation
287
- // Click a button in current month
288
- const currentMonthButtons = Array.from(buttons).filter(btn => {
289
- const cell = btn.closest('td[data-kt-datepicker-day]') as HTMLElement;
290
- const dateAttr = cell?.getAttribute('data-date');
291
- if (dateAttr) {
292
- const [year, month] = dateAttr.split('-').map(Number);
293
- return month - 1 === currentDate.getMonth();
294
- }
295
- return false;
296
- });
297
-
298
- if (currentMonthButtons.length > 0) {
299
- (currentMonthButtons[0] as HTMLButtonElement).click();
300
- expect(onDayClick).toHaveBeenCalled();
301
- }
302
- });
303
- });
304
-
305
- describe('Date Key Optimization', () => {
306
- it('should use date keys for selected date highlighting', () => {
307
- const days = getCalendarDays(2024, 0);
308
- const onDayClick = vi.fn();
309
- const currentDate = new Date(2024, 0, 15);
310
- const selectedDate = new Date(2024, 0, 15);
311
-
312
- const calendar = renderCalendar(
313
- defaultTemplates.dayCell as string,
314
- days,
315
- currentDate,
316
- selectedDate,
317
- onDayClick
318
- );
319
-
320
- // Find the selected cell
321
- const selectedCell = calendar.querySelector('[data-kt-selected="true"]') as HTMLElement;
322
- expect(selectedCell).toBeTruthy();
323
-
324
- // Verify it has the correct date
325
- const dateAttr = selectedCell.getAttribute('data-date');
326
- expect(dateAttr).toBe('2024-01-15');
327
- });
328
-
329
- it('should use date keys for multi-date selection', () => {
330
- const days = getCalendarDays(2024, 0);
331
- const onDayClick = vi.fn();
332
- const currentDate = new Date(2024, 0, 15);
333
- const selectedDates = [
334
- new Date(2024, 0, 10),
335
- new Date(2024, 0, 15),
336
- new Date(2024, 0, 20)
337
- ];
338
-
339
- const calendar = renderCalendar(
340
- defaultTemplates.dayCell as string,
341
- days,
342
- currentDate,
343
- null,
344
- onDayClick,
345
- 'en-US',
346
- undefined,
347
- selectedDates
348
- );
349
-
350
- // All selected dates should be highlighted
351
- const selectedCells = calendar.querySelectorAll('[data-kt-selected="true"]');
352
- expect(selectedCells.length).toBe(3);
353
- });
354
-
355
- it('should use date keys for range selection', () => {
356
- const days = getCalendarDays(2024, 0);
357
- const onDayClick = vi.fn();
358
- const currentDate = new Date(2024, 0, 15);
359
- const selectedRange = {
360
- start: new Date(2024, 0, 10),
361
- end: new Date(2024, 0, 20)
362
- };
363
-
364
- const calendar = renderCalendar(
365
- defaultTemplates.dayCell as string,
366
- days,
367
- currentDate,
368
- null,
369
- onDayClick,
370
- 'en-US',
371
- selectedRange
372
- );
373
-
374
- // Start and end dates should be highlighted
375
- const selectedCells = calendar.querySelectorAll('[data-kt-selected="true"]');
376
- expect(selectedCells.length).toBeGreaterThanOrEqual(2);
377
-
378
- // Range dates should have data-kt-hover-range attribute (consolidated from data-in-range)
379
- const inRangeCells = calendar.querySelectorAll('[data-kt-hover-range]');
380
- expect(inRangeCells.length).toBeGreaterThan(0);
381
- });
382
-
383
- it('should optimize tabbable index calculation using date keys', () => {
384
- const days = getCalendarDays(2024, 0);
385
- const onDayClick = vi.fn();
386
- const currentDate = new Date(2024, 0, 15);
387
- const selectedDate = new Date(2024, 0, 15);
388
-
389
- const calendar = renderCalendar(
390
- defaultTemplates.dayCell as string,
391
- days,
392
- currentDate,
393
- selectedDate,
394
- onDayClick
395
- );
396
-
397
- // Selected date should be tabbable (tabindex="0")
398
- const selectedCell = calendar.querySelector('[data-kt-selected="true"]') as HTMLElement;
399
- expect(selectedCell).toBeTruthy();
400
- const selectedButton = selectedCell?.querySelector('button[data-day]') as HTMLButtonElement;
401
- expect(selectedButton).toBeTruthy();
402
-
403
- // tabindex is set in the attributes string on the td, not directly on button
404
- // The button has tabindex="-1" by default in template, but the cell has tabindex="0" for selected
405
- // Check that the cell has the correct tabindex attribute
406
- const cellTabindex = selectedCell.getAttribute('tabindex');
407
- // The tabindex should be 0 for the selected date (set in attributes)
408
- // Since attributes are in the td, we check the cell
409
- expect(cellTabindex === '0' || selectedCell.tabIndex === 0).toBe(true);
410
- });
411
- });
412
-
413
- describe('Cell Reference Caching', () => {
414
- it('should cache cell references for hover range updates', () => {
415
- const days = getCalendarDays(2024, 0);
416
- const onDayClick = vi.fn();
417
- const currentDate = new Date(2024, 0, 15);
418
- const selectedRange = {
419
- start: new Date(2024, 0, 10),
420
- end: null
421
- };
422
-
423
- const calendar = renderCalendar(
424
- defaultTemplates.dayCell as string,
425
- days,
426
- currentDate,
427
- null,
428
- onDayClick,
429
- 'en-US',
430
- selectedRange
431
- );
432
-
433
- // All cells should have data-date attributes for caching
434
- const cells = calendar.querySelectorAll('td[data-kt-datepicker-day]');
435
- expect(cells.length).toBeGreaterThan(0);
436
- cells.forEach(cell => {
437
- expect(cell.hasAttribute('data-date')).toBe(true);
438
- });
439
- });
440
-
441
- it('should use cached cell references for hover range', () => {
442
- const days = getCalendarDays(2024, 0);
443
- const onDayClick = vi.fn();
444
- const currentDate = new Date(2024, 0, 15);
445
- const selectedRange = {
446
- start: new Date(2024, 0, 10),
447
- end: null
448
- };
449
-
450
- const calendar = renderCalendar(
451
- defaultTemplates.dayCell as string,
452
- days,
453
- currentDate,
454
- null,
455
- onDayClick,
456
- 'en-US',
457
- selectedRange
458
- );
459
-
460
- // Simulate hover on a date in range preview mode
461
- const hoverCell = calendar.querySelector(`[data-date="2024-01-15"]`) as HTMLElement;
462
- const hoverButton = hoverCell?.querySelector('button[data-day]') as HTMLButtonElement;
463
-
464
- if (hoverButton) {
465
- const mouseoverEvent = new MouseEvent('mouseover', { bubbles: true });
466
- hoverButton.dispatchEvent(mouseoverEvent);
467
-
468
- // Should have hover range attributes set (if in range preview mode)
469
- // Note: This depends on the range state being set correctly
470
- const hoverRangeCells = calendar.querySelectorAll('[data-kt-hover-range]');
471
- // May be 0 if range preview mode conditions aren't met, but cell should exist
472
- expect(hoverCell).toBeTruthy();
473
- }
474
- });
475
- });
476
-
477
- describe('Date Object Reuse', () => {
478
- it('should cache today date object', () => {
479
- const days = getCalendarDays(2024, 0);
480
- const onDayClick = vi.fn();
481
- const currentDate = new Date(2024, 0, 15);
482
-
483
- const calendar = renderCalendar(
484
- defaultTemplates.dayCell as string,
485
- days,
486
- currentDate,
487
- null,
488
- onDayClick
489
- );
490
-
491
- // Today's date should be marked if it's in the visible month
492
- const today = new Date();
493
- if (today.getMonth() === currentDate.getMonth() && today.getFullYear() === currentDate.getFullYear()) {
494
- const todayKey = getDateKey(today);
495
- const todayDateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
496
- const todayCell = calendar.querySelector(`[data-date="${todayDateStr}"]`) as HTMLElement;
497
- if (todayCell) {
498
- expect(todayCell.hasAttribute('data-today')).toBe(true);
499
- }
500
- }
501
- });
502
-
503
- it('should use date keys for range normalization instead of creating Date objects', () => {
504
- const days = getCalendarDays(2024, 0);
505
- const onDayClick = vi.fn();
506
- const currentDate = new Date(2024, 0, 15);
507
- const selectedRange = {
508
- start: new Date(2024, 0, 10),
509
- end: new Date(2024, 0, 20)
510
- };
511
-
512
- const calendar = renderCalendar(
513
- defaultTemplates.dayCell as string,
514
- days,
515
- currentDate,
516
- null,
517
- onDayClick,
518
- 'en-US',
519
- selectedRange
520
- );
521
-
522
- // Range dates should be marked without creating Date objects in loop
523
- // Note: Uses data-kt-hover-range (consolidated from data-in-range)
524
- const inRangeCells = calendar.querySelectorAll('[data-kt-hover-range]');
525
- expect(inRangeCells.length).toBeGreaterThan(0);
526
-
527
- // Verify range calculation is correct using date keys
528
- const startKey = getDateKey(selectedRange.start);
529
- const endKey = getDateKey(selectedRange.end);
530
- inRangeCells.forEach(cell => {
531
- const dateAttr = cell.getAttribute('data-date');
532
- if (dateAttr) {
533
- const [year, month, day] = dateAttr.split('-').map(Number);
534
- const cellKey = getDateKey(new Date(year, month - 1, day));
535
- expect(cellKey).toBeGreaterThanOrEqual(startKey);
536
- expect(cellKey).toBeLessThanOrEqual(endKey);
537
- }
538
- });
539
- });
540
- });
541
-
542
- describe('Performance Optimizations Integration', () => {
543
- it('should render calendar efficiently with all optimizations', () => {
544
- const days = getCalendarDays(2024, 0);
545
- const onDayClick = vi.fn();
546
- const currentDate = new Date(2024, 0, 15);
547
- const selectedDate = new Date(2024, 0, 15);
548
- const selectedDates = [
549
- new Date(2024, 0, 10),
550
- new Date(2024, 0, 20)
551
- ];
552
- const selectedRange = {
553
- start: new Date(2024, 0, 5),
554
- end: new Date(2024, 0, 25)
555
- };
556
-
557
- // Render with all selection modes to test optimizations
558
- const calendar = renderCalendar(
559
- defaultTemplates.dayCell as string,
560
- days,
561
- currentDate,
562
- selectedDate,
563
- onDayClick,
564
- 'en-US',
565
- selectedRange,
566
- selectedDates
567
- );
568
-
569
- // Verify calendar rendered correctly
570
- expect(calendar).toBeTruthy();
571
- const cells = calendar.querySelectorAll('td[data-kt-datepicker-day]');
572
- expect(cells.length).toBeGreaterThan(0);
573
-
574
- // Verify optimizations are working
575
- // 1. Day names should be cached (no way to directly test, but rendering works)
576
- // 2. Event delegation should work (tested separately)
577
- // 3. Date keys should work (selected dates highlighted)
578
- const selectedCells = calendar.querySelectorAll('[data-kt-selected="true"]');
579
- expect(selectedCells.length).toBeGreaterThan(0);
580
- });
581
-
582
- it('should handle multiple renders efficiently', () => {
583
- const days = getCalendarDays(2024, 0);
584
- const onDayClick = vi.fn();
585
- const currentDate = new Date(2024, 0, 15);
586
-
587
- // Render multiple times - day name cache should speed up subsequent renders
588
- const calendars: HTMLElement[] = [];
589
- for (let i = 0; i < 5; i++) {
590
- const calendar = renderCalendar(
591
- defaultTemplates.dayCell as string,
592
- days,
593
- currentDate,
594
- null,
595
- onDayClick,
596
- 'en-US'
597
- );
598
- calendars.push(calendar);
599
- }
600
-
601
- // All calendars should render correctly
602
- expect(calendars.length).toBe(5);
603
- calendars.forEach(cal => {
604
- expect(cal).toBeTruthy();
605
- const cells = cal.querySelectorAll('td[data-kt-datepicker-day]');
606
- expect(cells.length).toBeGreaterThan(0);
607
- });
608
- });
609
- });
610
- });
611
-