@keenthemes/ktui 1.0.28 → 1.1.0

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 (288) hide show
  1. package/README.md +27 -0
  2. package/dist/ktui.js +8780 -6199
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +2744 -1367
  6. package/lib/cjs/components/alert/alert.js +1025 -0
  7. package/lib/cjs/components/alert/alert.js.map +1 -0
  8. package/lib/cjs/components/alert/index.js +20 -0
  9. package/lib/cjs/components/alert/index.js.map +1 -0
  10. package/lib/cjs/components/alert/templates.js +120 -0
  11. package/lib/cjs/components/alert/templates.js.map +1 -0
  12. package/lib/cjs/components/alert/types.js +7 -0
  13. package/lib/cjs/components/alert/types.js.map +1 -0
  14. package/lib/cjs/components/datepicker/config/config.js +42 -0
  15. package/lib/cjs/components/datepicker/config/config.js.map +1 -0
  16. package/lib/cjs/components/datepicker/config/index.js +24 -0
  17. package/lib/cjs/components/datepicker/config/index.js.map +1 -0
  18. package/lib/cjs/components/datepicker/config/interfaces.js +7 -0
  19. package/lib/cjs/components/datepicker/config/interfaces.js.map +1 -0
  20. package/lib/cjs/components/datepicker/config/types.js +7 -0
  21. package/lib/cjs/components/datepicker/config/types.js.map +1 -0
  22. package/lib/cjs/components/datepicker/core/event-manager.js +135 -0
  23. package/lib/cjs/components/datepicker/core/event-manager.js.map +1 -0
  24. package/lib/cjs/components/datepicker/core/focus-manager.js +167 -0
  25. package/lib/cjs/components/datepicker/core/focus-manager.js.map +1 -0
  26. package/lib/cjs/components/datepicker/core/helpers.js +219 -0
  27. package/lib/cjs/components/datepicker/core/helpers.js.map +1 -0
  28. package/lib/cjs/components/datepicker/core/index.js +25 -0
  29. package/lib/cjs/components/datepicker/core/index.js.map +1 -0
  30. package/lib/cjs/components/datepicker/core/unified-state-manager.js +394 -0
  31. package/lib/cjs/components/datepicker/core/unified-state-manager.js.map +1 -0
  32. package/lib/cjs/components/datepicker/datepicker.js +2066 -763
  33. package/lib/cjs/components/datepicker/datepicker.js.map +1 -1
  34. package/lib/cjs/components/datepicker/index.js +19 -8
  35. package/lib/cjs/components/datepicker/index.js.map +1 -1
  36. package/lib/cjs/components/datepicker/ui/index.js +23 -0
  37. package/lib/cjs/components/datepicker/ui/index.js.map +1 -0
  38. package/lib/cjs/components/datepicker/ui/input/dropdown.js +489 -0
  39. package/lib/cjs/components/datepicker/ui/input/dropdown.js.map +1 -0
  40. package/lib/cjs/components/datepicker/ui/input/index.js +23 -0
  41. package/lib/cjs/components/datepicker/ui/input/index.js.map +1 -0
  42. package/lib/cjs/components/datepicker/ui/input/segmented-input.js +640 -0
  43. package/lib/cjs/components/datepicker/ui/input/segmented-input.js.map +1 -0
  44. package/lib/cjs/components/datepicker/ui/renderers/calendar.js +446 -0
  45. package/lib/cjs/components/datepicker/ui/renderers/calendar.js.map +1 -0
  46. package/lib/cjs/components/datepicker/ui/renderers/footer.js +42 -0
  47. package/lib/cjs/components/datepicker/ui/renderers/footer.js.map +1 -0
  48. package/lib/cjs/components/datepicker/ui/renderers/header.js +32 -0
  49. package/lib/cjs/components/datepicker/ui/renderers/header.js.map +1 -0
  50. package/lib/cjs/components/datepicker/ui/renderers/index.js +25 -0
  51. package/lib/cjs/components/datepicker/ui/renderers/index.js.map +1 -0
  52. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js +384 -0
  53. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js.map +1 -0
  54. package/lib/cjs/components/datepicker/ui/templates/index.js +22 -0
  55. package/lib/cjs/components/datepicker/ui/templates/index.js.map +1 -0
  56. package/lib/cjs/components/datepicker/ui/templates/templates.js +253 -0
  57. package/lib/cjs/components/datepicker/ui/templates/templates.js.map +1 -0
  58. package/lib/cjs/components/datepicker/utils/date-formatters.js +88 -0
  59. package/lib/cjs/components/datepicker/utils/date-formatters.js.map +1 -0
  60. package/lib/cjs/components/datepicker/utils/date-utils.js +194 -0
  61. package/lib/cjs/components/datepicker/utils/date-utils.js.map +1 -0
  62. package/lib/cjs/components/datepicker/utils/index.js +24 -0
  63. package/lib/cjs/components/datepicker/utils/index.js.map +1 -0
  64. package/lib/cjs/components/datepicker/utils/time-utils.js +213 -0
  65. package/lib/cjs/components/datepicker/utils/time-utils.js.map +1 -0
  66. package/lib/cjs/index.js +6 -1
  67. package/lib/cjs/index.js.map +1 -1
  68. package/lib/esm/components/alert/alert.js +1022 -0
  69. package/lib/esm/components/alert/alert.js.map +1 -0
  70. package/lib/esm/components/alert/index.js +4 -0
  71. package/lib/esm/components/alert/index.js.map +1 -0
  72. package/lib/esm/components/alert/templates.js +112 -0
  73. package/lib/esm/components/alert/templates.js.map +1 -0
  74. package/lib/esm/components/alert/types.js +6 -0
  75. package/lib/esm/components/alert/types.js.map +1 -0
  76. package/lib/esm/components/datepicker/config/config.js +39 -0
  77. package/lib/esm/components/datepicker/config/config.js.map +1 -0
  78. package/lib/esm/components/datepicker/config/index.js +8 -0
  79. package/lib/esm/components/datepicker/config/index.js.map +1 -0
  80. package/lib/esm/components/datepicker/config/interfaces.js +6 -0
  81. package/lib/esm/components/datepicker/config/interfaces.js.map +1 -0
  82. package/lib/esm/components/datepicker/config/types.js +6 -0
  83. package/lib/esm/components/datepicker/config/types.js.map +1 -0
  84. package/lib/esm/components/datepicker/core/event-manager.js +133 -0
  85. package/lib/esm/components/datepicker/core/event-manager.js.map +1 -0
  86. package/lib/esm/components/datepicker/core/focus-manager.js +164 -0
  87. package/lib/esm/components/datepicker/core/focus-manager.js.map +1 -0
  88. package/lib/esm/components/datepicker/core/helpers.js +211 -0
  89. package/lib/esm/components/datepicker/core/helpers.js.map +1 -0
  90. package/lib/esm/components/datepicker/core/index.js +9 -0
  91. package/lib/esm/components/datepicker/core/index.js.map +1 -0
  92. package/lib/esm/components/datepicker/core/unified-state-manager.js +391 -0
  93. package/lib/esm/components/datepicker/core/unified-state-manager.js.map +1 -0
  94. package/lib/esm/components/datepicker/datepicker.js +2065 -763
  95. package/lib/esm/components/datepicker/datepicker.js.map +1 -1
  96. package/lib/esm/components/datepicker/index.js +6 -8
  97. package/lib/esm/components/datepicker/index.js.map +1 -1
  98. package/lib/esm/components/datepicker/ui/index.js +7 -0
  99. package/lib/esm/components/datepicker/ui/index.js.map +1 -0
  100. package/lib/esm/components/datepicker/ui/input/dropdown.js +486 -0
  101. package/lib/esm/components/datepicker/ui/input/dropdown.js.map +1 -0
  102. package/lib/esm/components/datepicker/ui/input/index.js +7 -0
  103. package/lib/esm/components/datepicker/ui/input/index.js.map +1 -0
  104. package/lib/esm/components/datepicker/ui/input/segmented-input.js +637 -0
  105. package/lib/esm/components/datepicker/ui/input/segmented-input.js.map +1 -0
  106. package/lib/esm/components/datepicker/ui/renderers/calendar.js +443 -0
  107. package/lib/esm/components/datepicker/ui/renderers/calendar.js.map +1 -0
  108. package/lib/esm/components/datepicker/ui/renderers/footer.js +39 -0
  109. package/lib/esm/components/datepicker/ui/renderers/footer.js.map +1 -0
  110. package/lib/esm/components/datepicker/ui/renderers/header.js +29 -0
  111. package/lib/esm/components/datepicker/ui/renderers/header.js.map +1 -0
  112. package/lib/esm/components/datepicker/ui/renderers/index.js +9 -0
  113. package/lib/esm/components/datepicker/ui/renderers/index.js.map +1 -0
  114. package/lib/esm/components/datepicker/ui/renderers/time-picker.js +381 -0
  115. package/lib/esm/components/datepicker/ui/renderers/time-picker.js.map +1 -0
  116. package/lib/esm/components/datepicker/ui/templates/index.js +6 -0
  117. package/lib/esm/components/datepicker/ui/templates/index.js.map +1 -0
  118. package/lib/esm/components/datepicker/ui/templates/templates.js +242 -0
  119. package/lib/esm/components/datepicker/ui/templates/templates.js.map +1 -0
  120. package/lib/esm/components/datepicker/utils/date-formatters.js +83 -0
  121. package/lib/esm/components/datepicker/utils/date-formatters.js.map +1 -0
  122. package/lib/esm/components/datepicker/utils/date-utils.js +184 -0
  123. package/lib/esm/components/datepicker/utils/date-utils.js.map +1 -0
  124. package/lib/esm/components/datepicker/utils/index.js +8 -0
  125. package/lib/esm/components/datepicker/utils/index.js.map +1 -0
  126. package/lib/esm/components/datepicker/utils/time-utils.js +201 -0
  127. package/lib/esm/components/datepicker/utils/time-utils.js.map +1 -0
  128. package/lib/esm/index.js +4 -0
  129. package/lib/esm/index.js.map +1 -1
  130. package/package.json +22 -3
  131. package/src/components/alert/alert.css +429 -188
  132. package/src/components/alert/alert.ts +990 -0
  133. package/src/components/alert/index.ts +4 -0
  134. package/src/components/alert/templates.ts +110 -0
  135. package/src/components/alert/tests/accessibility/aria-roles.test.ts +19 -0
  136. package/src/components/alert/tests/accessibility/focus-management.test.ts +19 -0
  137. package/src/components/alert/tests/accessibility/keyboard-nav.test.ts +22 -0
  138. package/src/components/alert/tests/actions/confirm-cancel.test.ts +122 -0
  139. package/src/components/alert/tests/actions/input-field.test.ts +180 -0
  140. package/src/components/alert/tests/alert.basic.test.ts +126 -0
  141. package/src/components/alert/tests/alert.config.test.ts +75 -0
  142. package/src/components/alert/tests/alert.templates.test.ts +17 -0
  143. package/src/components/alert/tests/config/attribute-config.test.ts +94 -0
  144. package/src/components/alert/tests/config/json-config.test.ts +119 -0
  145. package/src/components/alert/tests/config/merging.test.ts +89 -0
  146. package/src/components/alert/tests/dismissal/auto-dismiss.test.ts +96 -0
  147. package/src/components/alert/tests/dismissal/escape-key-dismiss.test.ts +105 -0
  148. package/src/components/alert/tests/dismissal/manual-dismiss.test.ts +90 -0
  149. package/src/components/alert/tests/dismissal/outside-click-dismiss.test.ts +91 -0
  150. package/src/components/alert/tests/edge-cases/invalid-config.test.ts +19 -0
  151. package/src/components/alert/tests/edge-cases/multiple-alerts.test.ts +19 -0
  152. package/src/components/alert/tests/rendering/custom-content.test.ts +81 -0
  153. package/src/components/alert/tests/rendering/info-alert.test.ts +84 -0
  154. package/src/components/alert/tests/rendering/success-alert.test.ts +100 -0
  155. package/src/components/alert/tests/templates/default-templates.test.ts +16 -0
  156. package/src/components/alert/tests/templates/user-templates.test.ts +16 -0
  157. package/src/components/alert/types.ts +145 -0
  158. package/src/components/datepicker/__tests__/datepicker-events.test.ts +356 -0
  159. package/src/components/datepicker/__tests__/datepicker-init.test.ts +343 -0
  160. package/src/components/datepicker/__tests__/datepicker-integration.test.ts +435 -0
  161. package/src/components/datepicker/__tests__/datepicker-timezone.test.ts +220 -0
  162. package/src/components/datepicker/__tests__/segmented-input-focus.test.ts +380 -0
  163. package/src/components/datepicker/__tests__/selective-state-updates.test.ts +400 -0
  164. package/src/components/datepicker/__tests__/state-manager.test.ts +421 -0
  165. package/src/components/datepicker/__tests__/time-preservation.test.ts +387 -0
  166. package/src/components/datepicker/config/config.ts +40 -0
  167. package/src/components/datepicker/config/index.ts +8 -0
  168. package/src/components/datepicker/config/interfaces.ts +82 -0
  169. package/src/components/datepicker/config/types.ts +188 -0
  170. package/src/components/datepicker/core/event-manager.ts +159 -0
  171. package/src/components/datepicker/core/focus-manager.ts +201 -0
  172. package/src/components/datepicker/core/helpers.ts +231 -0
  173. package/src/components/datepicker/core/index.ts +9 -0
  174. package/src/components/datepicker/core/unified-state-manager.ts +459 -0
  175. package/src/components/datepicker/datepicker.css +429 -1
  176. package/src/components/datepicker/datepicker.ts +2538 -1277
  177. package/src/components/datepicker/index.ts +6 -8
  178. package/src/components/datepicker/ui/index.ts +7 -0
  179. package/src/components/datepicker/ui/input/dropdown.ts +552 -0
  180. package/src/components/datepicker/ui/input/index.ts +7 -0
  181. package/src/components/datepicker/ui/input/segmented-input.ts +638 -0
  182. package/src/components/datepicker/ui/renderers/__tests__/calendar-optimizations.test.ts +611 -0
  183. package/src/components/datepicker/ui/renderers/calendar.ts +530 -0
  184. package/src/components/datepicker/ui/renderers/footer.ts +43 -0
  185. package/src/components/datepicker/ui/renderers/header.ts +33 -0
  186. package/src/components/datepicker/ui/renderers/index.ts +9 -0
  187. package/src/components/datepicker/ui/renderers/time-picker.ts +438 -0
  188. package/src/components/datepicker/ui/templates/index.ts +6 -0
  189. package/src/components/datepicker/ui/templates/templates.ts +306 -0
  190. package/src/components/datepicker/utils/__tests__/date-formatters.test.ts +160 -0
  191. package/src/components/datepicker/utils/__tests__/date-utils-keys.test.ts +86 -0
  192. package/src/components/datepicker/utils/__tests__/date-utils-timezone.test.ts +215 -0
  193. package/src/components/datepicker/utils/date-formatters.ts +85 -0
  194. package/src/components/datepicker/utils/date-utils.ts +172 -0
  195. package/src/components/datepicker/utils/index.ts +8 -0
  196. package/src/components/datepicker/utils/time-utils.ts +221 -0
  197. package/src/index.ts +7 -1
  198. package/CONTRIBUTING.md +0 -101
  199. package/examples/datatable/checkbox-events-test.html +0 -400
  200. package/examples/datatable/credentials-test.html +0 -423
  201. package/examples/datatable/remote-checkbox-test.html +0 -365
  202. package/examples/datatable/sorting-test.html +0 -258
  203. package/examples/image-input/file-upload-example.html +0 -189
  204. package/examples/modal/persistent.html +0 -205
  205. package/examples/modal/remote-select-dropdown.html +0 -166
  206. package/examples/modal/select-dropdown-container.html +0 -129
  207. package/examples/select/avatar.html +0 -47
  208. package/examples/select/basic-usage.html +0 -39
  209. package/examples/select/country.html +0 -43
  210. package/examples/select/dark-mode.html +0 -93
  211. package/examples/select/description.html +0 -53
  212. package/examples/select/disable-option.html +0 -37
  213. package/examples/select/disable-select.html +0 -35
  214. package/examples/select/dropdowncontainer.html +0 -111
  215. package/examples/select/dynamic-methods.html +0 -273
  216. package/examples/select/formdata-remote.html +0 -161
  217. package/examples/select/global-config.html +0 -81
  218. package/examples/select/icon-multiple.html +0 -50
  219. package/examples/select/icon.html +0 -48
  220. package/examples/select/max-selection.html +0 -38
  221. package/examples/select/modal-container.html +0 -128
  222. package/examples/select/modal-positioning-test.html +0 -338
  223. package/examples/select/modal.html +0 -80
  224. package/examples/select/multiple.html +0 -40
  225. package/examples/select/native-selected.html +0 -64
  226. package/examples/select/placeholder.html +0 -40
  227. package/examples/select/remote-data-preselected.html +0 -283
  228. package/examples/select/remote-data.html +0 -38
  229. package/examples/select/search.html +0 -57
  230. package/examples/select/sizes.html +0 -94
  231. package/examples/select/tags-enhanced.html +0 -86
  232. package/examples/select/tags-icons.html +0 -57
  233. package/examples/select/template-customization.html +0 -61
  234. package/examples/sticky/README.md +0 -158
  235. package/examples/sticky/debug-sticky.html +0 -144
  236. package/examples/sticky/test-runner.html +0 -175
  237. package/examples/sticky/test-sticky-logic.js +0 -369
  238. package/examples/sticky/test-sticky-positioning.html +0 -386
  239. package/examples/toast/example.html +0 -479
  240. package/lib/cjs/components/datepicker/calendar.js +0 -1061
  241. package/lib/cjs/components/datepicker/calendar.js.map +0 -1
  242. package/lib/cjs/components/datepicker/config.js +0 -332
  243. package/lib/cjs/components/datepicker/config.js.map +0 -1
  244. package/lib/cjs/components/datepicker/dropdown.js +0 -635
  245. package/lib/cjs/components/datepicker/dropdown.js.map +0 -1
  246. package/lib/cjs/components/datepicker/events.js +0 -129
  247. package/lib/cjs/components/datepicker/events.js.map +0 -1
  248. package/lib/cjs/components/datepicker/keyboard.js +0 -536
  249. package/lib/cjs/components/datepicker/keyboard.js.map +0 -1
  250. package/lib/cjs/components/datepicker/locales.js +0 -78
  251. package/lib/cjs/components/datepicker/locales.js.map +0 -1
  252. package/lib/cjs/components/datepicker/templates.js +0 -403
  253. package/lib/cjs/components/datepicker/templates.js.map +0 -1
  254. package/lib/cjs/components/datepicker/types.js +0 -23
  255. package/lib/cjs/components/datepicker/types.js.map +0 -1
  256. package/lib/cjs/components/datepicker/utils.js +0 -524
  257. package/lib/cjs/components/datepicker/utils.js.map +0 -1
  258. package/lib/esm/components/datepicker/calendar.js +0 -1058
  259. package/lib/esm/components/datepicker/calendar.js.map +0 -1
  260. package/lib/esm/components/datepicker/config.js +0 -329
  261. package/lib/esm/components/datepicker/config.js.map +0 -1
  262. package/lib/esm/components/datepicker/dropdown.js +0 -632
  263. package/lib/esm/components/datepicker/dropdown.js.map +0 -1
  264. package/lib/esm/components/datepicker/events.js +0 -126
  265. package/lib/esm/components/datepicker/events.js.map +0 -1
  266. package/lib/esm/components/datepicker/keyboard.js +0 -533
  267. package/lib/esm/components/datepicker/keyboard.js.map +0 -1
  268. package/lib/esm/components/datepicker/locales.js +0 -74
  269. package/lib/esm/components/datepicker/locales.js.map +0 -1
  270. package/lib/esm/components/datepicker/templates.js +0 -390
  271. package/lib/esm/components/datepicker/templates.js.map +0 -1
  272. package/lib/esm/components/datepicker/types.js +0 -20
  273. package/lib/esm/components/datepicker/types.js.map +0 -1
  274. package/lib/esm/components/datepicker/utils.js +0 -508
  275. package/lib/esm/components/datepicker/utils.js.map +0 -1
  276. package/prettier.config.js +0 -9
  277. package/src/components/datepicker/calendar.ts +0 -1397
  278. package/src/components/datepicker/config.ts +0 -368
  279. package/src/components/datepicker/dropdown.ts +0 -757
  280. package/src/components/datepicker/events.ts +0 -149
  281. package/src/components/datepicker/keyboard.ts +0 -646
  282. package/src/components/datepicker/locales.ts +0 -80
  283. package/src/components/datepicker/templates.ts +0 -792
  284. package/src/components/datepicker/types.ts +0 -154
  285. package/src/components/datepicker/utils.ts +0 -631
  286. package/src/components/select/variants.css +0 -4
  287. package/tsconfig.json +0 -17
  288. package/webpack.config.js +0 -118
@@ -0,0 +1,435 @@
1
+ /**
2
+ * datepicker-integration.test.ts - Integration tests for KTDatepicker
3
+ * Tests backward compatibility, configuration options, and real-world usage patterns
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
+ import { KTDatepicker } from '../datepicker';
8
+ import { KTDatepickerConfig } from '../config/types';
9
+
10
+ describe('KTDatepicker Integration', () => {
11
+ let element: HTMLElement;
12
+
13
+ beforeEach(() => {
14
+ // Create a fresh element for each test
15
+ element = document.createElement('div');
16
+ element.innerHTML = `
17
+ <div class="kt-datepicker" data-kt-datepicker-segmented>
18
+ <input type="text" data-kt-datepicker-input placeholder="Select date">
19
+ </div>
20
+ `;
21
+
22
+ // Clear any existing content and add our test element
23
+ document.body.innerHTML = '';
24
+ document.body.appendChild(element);
25
+ });
26
+
27
+ describe('Configuration Options', () => {
28
+ it('should accept all configuration options without errors', () => {
29
+ const config: KTDatepickerConfig = {
30
+ format: 'dd/MM/yyyy',
31
+ range: false,
32
+ multiDate: false,
33
+ minDate: new Date(2024, 0, 1),
34
+ maxDate: new Date(2024, 11, 31),
35
+ disabled: false,
36
+ locale: 'en-US',
37
+ placeholder: 'Select a date',
38
+ value: new Date(2024, 0, 15),
39
+ showOnFocus: true,
40
+ closeOnSelect: true,
41
+ closeOnOutsideClick: true,
42
+ visibleMonths: 1,
43
+ enableTime: false,
44
+ timeGranularity: 'minute',
45
+ timeFormat: '24h',
46
+ minTime: '00:00',
47
+ maxTime: '23:59',
48
+ timeStep: 1,
49
+ dropdownPlacement: 'bottom-start',
50
+ dropdownOffset: '0,8',
51
+ dropdownBoundary: 'clippingParents',
52
+ dropdownWidth: 'auto',
53
+ dropdownZindex: 9999,
54
+ classes: {
55
+ container: 'custom-container',
56
+ inputWrapper: 'custom-input-wrapper'
57
+ },
58
+ onChange: vi.fn(),
59
+ onOpen: vi.fn(),
60
+ onClose: vi.fn()
61
+ };
62
+
63
+ expect(() => {
64
+ new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
65
+ }).not.toThrow();
66
+ });
67
+
68
+ it('should work with minimal configuration', () => {
69
+ const config: KTDatepickerConfig = {
70
+ format: 'yyyy-MM-dd'
71
+ };
72
+
73
+ expect(() => {
74
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
75
+ expect(datepicker).toBeDefined();
76
+ }).not.toThrow();
77
+ });
78
+
79
+ it('should handle undefined configuration', () => {
80
+ expect(() => {
81
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, undefined);
82
+ expect(datepicker).toBeDefined();
83
+ }).not.toThrow();
84
+ });
85
+ });
86
+
87
+ describe('DOM Integration', () => {
88
+ it('should create proper DOM structure', () => {
89
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!);
90
+
91
+ // Should have created input wrapper
92
+ const inputWrapper = element.querySelector('[data-kt-datepicker-input-wrapper]');
93
+ expect(inputWrapper).toBeTruthy();
94
+
95
+ // Should have segmented input elements
96
+ const segments = element.querySelectorAll('[data-segment]');
97
+ expect(segments.length).toBeGreaterThan(0);
98
+
99
+ // Dropdown is created lazily when opened
100
+ datepicker.open();
101
+ // Wait a bit for DOM updates
102
+ return new Promise(resolve => {
103
+ setTimeout(() => {
104
+ const dropdown = document.querySelector('[data-kt-datepicker-dropdown]');
105
+ expect(dropdown).toBeTruthy();
106
+ resolve(void 0);
107
+ }, 10);
108
+ });
109
+ });
110
+
111
+ it('should handle existing input element', () => {
112
+ // Element already has input in beforeEach
113
+ const existingInput = element.querySelector('input');
114
+ expect(existingInput).toBeTruthy(); // Input should exist before datepicker creation
115
+
116
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!);
117
+
118
+ // In segmented mode, the input is processed and segmented UI is created
119
+ // Check that segmented input elements were created (this is the expected behavior)
120
+ const segmentedElements = element.querySelectorAll('[data-segment]');
121
+ expect(segmentedElements.length).toBeGreaterThan(0);
122
+
123
+ // Verify datepicker was created successfully
124
+ expect(datepicker).toBeDefined();
125
+ });
126
+
127
+ it('should create input element if none exists', () => {
128
+ // Create a fresh element without input
129
+ const testElement = document.createElement('div');
130
+ testElement.innerHTML = `
131
+ <div class="kt-datepicker" data-kt-datepicker-segmented>
132
+ </div>
133
+ `;
134
+ document.body.appendChild(testElement);
135
+
136
+ const datepicker = new KTDatepicker(testElement.querySelector('.kt-datepicker')!);
137
+
138
+ // Should have created new input - it might be in the document root or moved
139
+ const newInput = document.querySelector('input[data-kt-datepicker-input]');
140
+ expect(newInput).toBeTruthy();
141
+
142
+ // Cleanup
143
+ document.body.removeChild(testElement);
144
+ });
145
+ });
146
+
147
+ describe('Data Attributes', () => {
148
+ it('should read data attributes for configuration', () => {
149
+ element.innerHTML = `
150
+ <div class="kt-datepicker"
151
+ data-kt-datepicker-segmented
152
+ data-kt-datepicker-show-on-focus="false"
153
+ data-kt-datepicker-close-on-select="false"
154
+ data-kt-datepicker-enable-time="true"
155
+ data-kt-datepicker-range="true"
156
+ data-kt-datepicker-close-on-outside-click="false">
157
+ <input type="text" data-kt-datepicker-input>
158
+ </div>
159
+ `;
160
+
161
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!);
162
+
163
+ // Note: We can't easily test internal config, but the component should initialize without errors
164
+ expect(datepicker).toBeDefined();
165
+ });
166
+
167
+ it('should parse JSON config from data attribute', () => {
168
+ const jsonConfig = {
169
+ format: 'MM/dd/yyyy',
170
+ disabled: true,
171
+ placeholder: 'Custom placeholder'
172
+ };
173
+
174
+ element.innerHTML = `
175
+ <div class="kt-datepicker"
176
+ data-kt-datepicker-config='${JSON.stringify(jsonConfig)}'>
177
+ <input type="text" data-kt-datepicker-input>
178
+ </div>
179
+ `;
180
+
181
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!);
182
+
183
+ expect(datepicker).toBeDefined();
184
+ });
185
+
186
+ it('should handle invalid JSON config gracefully', () => {
187
+ element.innerHTML = `
188
+ <div class="kt-datepicker"
189
+ data-kt-datepicker-config='{invalid json}'>
190
+ <input type="text" data-kt-datepicker-input>
191
+ </div>
192
+ `;
193
+
194
+ expect(() => {
195
+ new KTDatepicker(element.querySelector('.kt-datepicker')!);
196
+ }).not.toThrow();
197
+ });
198
+ });
199
+
200
+ describe('Range Mode', () => {
201
+ it('should initialize in range mode', () => {
202
+ const config: KTDatepickerConfig = {
203
+ range: true,
204
+ format: 'dd/MM/yyyy'
205
+ };
206
+
207
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
208
+
209
+ expect(datepicker).toBeDefined();
210
+ });
211
+
212
+ it('should handle range value initialization', () => {
213
+ const config: KTDatepickerConfig = {
214
+ range: true,
215
+ format: 'dd/MM/yyyy',
216
+ valueRange: {
217
+ start: new Date(2024, 0, 15),
218
+ end: new Date(2024, 0, 20)
219
+ }
220
+ };
221
+
222
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
223
+
224
+ expect(datepicker).toBeDefined();
225
+ });
226
+ });
227
+
228
+ describe('Multi-Date Mode', () => {
229
+ it('should initialize in multi-date mode', () => {
230
+ const config: KTDatepickerConfig = {
231
+ multiDate: true,
232
+ format: 'dd/MM/yyyy'
233
+ };
234
+
235
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
236
+
237
+ expect(datepicker).toBeDefined();
238
+ });
239
+
240
+ it('should handle multiple values initialization', () => {
241
+ const config: KTDatepickerConfig = {
242
+ multiDate: true,
243
+ format: 'dd/MM/yyyy',
244
+ values: [
245
+ new Date(2024, 0, 15),
246
+ new Date(2024, 0, 20),
247
+ new Date(2024, 0, 25)
248
+ ]
249
+ };
250
+
251
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
252
+
253
+ expect(datepicker).toBeDefined();
254
+ });
255
+ });
256
+
257
+ describe('Time Picker', () => {
258
+ it('should initialize with time picker enabled', () => {
259
+ const config: KTDatepickerConfig = {
260
+ enableTime: true,
261
+ timeFormat: '12h',
262
+ format: 'dd/MM/yyyy'
263
+ };
264
+
265
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
266
+
267
+ expect(datepicker).toBeDefined();
268
+ });
269
+
270
+ it('should handle time constraints', () => {
271
+ const config: KTDatepickerConfig = {
272
+ enableTime: true,
273
+ format: 'dd/MM/yyyy',
274
+ minTime: '09:00',
275
+ maxTime: '17:00',
276
+ timeStep: 30
277
+ };
278
+
279
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
280
+
281
+ expect(datepicker).toBeDefined();
282
+ });
283
+
284
+ it('should preserve time when selecting dates from calendar after setting time via segmented inputs', () => {
285
+ // Set up datepicker with time enabled
286
+ const config: KTDatepickerConfig = {
287
+ enableTime: true,
288
+ timeFormat: '12h',
289
+ format: 'dd/MM/yyyy',
290
+ value: new Date(2024, 10, 15, 10, 30) // Nov 15, 2024, 10:30 AM
291
+ };
292
+
293
+ const datepickerElement = element.querySelector('.kt-datepicker')!;
294
+ const datepicker = new KTDatepicker(datepickerElement, config);
295
+
296
+ // Wait for initialization
297
+ expect(datepicker).toBeDefined();
298
+
299
+ return new Promise<void>((resolve) => {
300
+ setTimeout(() => {
301
+ // Simulate segmented input changing time to 3:45 PM
302
+ const newDateTime = new Date(2024, 10, 15, 15, 45); // Nov 15, 2024, 3:45 PM
303
+ (datepicker as any)._handleSegmentedInputChange(newDateTime);
304
+
305
+ // Wait for state update and verify selectedTime was updated
306
+ setTimeout(() => {
307
+ const updatedState = (datepicker as any)._unifiedStateManager.getState();
308
+ expect(updatedState.selectedTime).toBeTruthy();
309
+ expect(updatedState.selectedTime.hour).toBe(15); // 3 PM in 24-hour format
310
+ expect(updatedState.selectedTime.minute).toBe(45);
311
+ expect(updatedState.selectedDate.getHours()).toBe(15);
312
+ expect(updatedState.selectedDate.getMinutes()).toBe(45);
313
+
314
+ // Now simulate calendar date selection (November 20th)
315
+ const newDate = new Date(2024, 10, 20); // Nov 20, 2024
316
+ datepicker.setDate(newDate);
317
+
318
+ // Wait for update and verify time is still preserved
319
+ setTimeout(() => {
320
+ const finalState = (datepicker as any)._unifiedStateManager.getState();
321
+ expect(finalState.selectedTime).toBeTruthy();
322
+ expect(finalState.selectedTime.hour).toBe(15); // Time should still be 3:45 PM
323
+ expect(finalState.selectedTime.minute).toBe(45);
324
+ expect(finalState.selectedDate.getDate()).toBe(20); // Date should be 20th
325
+ expect(finalState.selectedDate.getHours()).toBe(15); // Time should be preserved
326
+ expect(finalState.selectedDate.getMinutes()).toBe(45);
327
+
328
+ resolve();
329
+ }, 50);
330
+ }, 50);
331
+ }, 100);
332
+ });
333
+ });
334
+ });
335
+
336
+ describe('Constraints', () => {
337
+ it('should handle min/max date constraints', () => {
338
+ const config: KTDatepickerConfig = {
339
+ format: 'dd/MM/yyyy',
340
+ minDate: new Date(2024, 0, 1),
341
+ maxDate: new Date(2024, 11, 31)
342
+ };
343
+
344
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
345
+
346
+ expect(datepicker).toBeDefined();
347
+ });
348
+
349
+ it('should handle disabled dates array', () => {
350
+ const config: KTDatepickerConfig = {
351
+ format: 'dd/MM/yyyy',
352
+ disabledDates: [
353
+ new Date(2024, 0, 1), // New Year's Day
354
+ new Date(2024, 0, 15), // Some holiday
355
+ ]
356
+ };
357
+
358
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!, config);
359
+
360
+ expect(datepicker).toBeDefined();
361
+ });
362
+ });
363
+
364
+ describe('Multiple Instances', () => {
365
+ it('should support multiple independent datepickers', () => {
366
+ // Create first datepicker
367
+ const datepicker1 = new KTDatepicker(element.querySelector('.kt-datepicker')!, {
368
+ format: 'dd/MM/yyyy'
369
+ });
370
+
371
+ // Create second datepicker element
372
+ const element2 = document.createElement('div');
373
+ element2.innerHTML = `
374
+ <div class="kt-datepicker" data-kt-datepicker-segmented>
375
+ <input type="text" data-kt-datepicker-input placeholder="Select date 2">
376
+ </div>
377
+ `;
378
+ document.body.appendChild(element2);
379
+
380
+ // Create second datepicker
381
+ const datepicker2 = new KTDatepicker(element2.querySelector('.kt-datepicker')!, {
382
+ format: 'yyyy-MM-dd'
383
+ });
384
+
385
+ // Both should exist independently
386
+ expect(datepicker1).toBeDefined();
387
+ expect(datepicker2).toBeDefined();
388
+
389
+ // Cleanup
390
+ document.body.removeChild(element2);
391
+ });
392
+ });
393
+
394
+ describe('Error Recovery', () => {
395
+ it('should handle invalid element gracefully', () => {
396
+ expect(() => {
397
+ new KTDatepicker(null as any);
398
+ }).toThrow(); // Should throw for null element
399
+ });
400
+
401
+ it('should handle missing DOM methods gracefully', () => {
402
+ // Create element without proper DOM methods
403
+ const mockElement = {
404
+ querySelector: () => null,
405
+ setAttribute: vi.fn(),
406
+ getAttribute: vi.fn()
407
+ };
408
+
409
+ expect(() => {
410
+ new KTDatepicker(mockElement as any);
411
+ }).toThrow(); // Should throw when DOM methods fail
412
+ });
413
+ });
414
+
415
+ describe('Memory Management', () => {
416
+ it('should clean up event listeners on destruction', () => {
417
+ const datepicker = new KTDatepicker(element.querySelector('.kt-datepicker')!);
418
+
419
+ // Mock the event manager's removeListener method
420
+ const mockRemoveListener = vi.fn();
421
+ (datepicker as any)._eventManager = {
422
+ removeListener: mockRemoveListener,
423
+ addListener: vi.fn()
424
+ };
425
+
426
+ // Trigger cleanup (this would normally happen on destruction)
427
+ if ((datepicker as any).dispose) {
428
+ (datepicker as any).dispose();
429
+ }
430
+
431
+ // Event listeners should be cleaned up
432
+ // Note: This test assumes a dispose method exists or will be added
433
+ });
434
+ });
435
+ });
@@ -0,0 +1,220 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { KTDatepicker } from '../datepicker';
3
+ import { formatDateToLocalString, parseLocalDate } from '../utils/date-utils';
4
+
5
+ describe('KTDatepicker - Timezone Handling', () => {
6
+ let container: HTMLElement;
7
+
8
+ beforeEach(() => {
9
+ container = document.createElement('div');
10
+ container.setAttribute('data-kt-datepicker', '');
11
+ document.body.appendChild(container);
12
+ });
13
+
14
+ afterEach(() => {
15
+ if (container.parentNode) {
16
+ document.body.removeChild(container);
17
+ }
18
+ });
19
+
20
+ describe('Date Selection with Local Timezone', () => {
21
+ it('should store selected date in local timezone', () => {
22
+ const datepicker = new KTDatepicker(container);
23
+ const testDate = new Date(2024, 0, 15, 14, 30, 0); // Jan 15, 2024 2:30 PM
24
+
25
+ datepicker.setDate(testDate);
26
+ const state = datepicker.getState();
27
+
28
+ expect(state.selectedDate).toBeDefined();
29
+ if (state.selectedDate) {
30
+ // Date should preserve the local date components
31
+ expect(state.selectedDate.getFullYear()).toBe(2024);
32
+ expect(state.selectedDate.getMonth()).toBe(0);
33
+ expect(state.selectedDate.getDate()).toBe(15);
34
+ }
35
+ });
36
+
37
+ it('should not shift dates when selecting late evening times', () => {
38
+ const datepicker = new KTDatepicker(container);
39
+ // Late evening that could shift to next day in UTC
40
+ const lateEvening = new Date(2024, 0, 15, 23, 30, 0); // Jan 15, 11:30 PM
41
+
42
+ datepicker.setDate(lateEvening);
43
+ const state = datepicker.getState();
44
+
45
+ expect(state.selectedDate).toBeDefined();
46
+ if (state.selectedDate) {
47
+ // Should still be Jan 15, not shifted to Jan 16
48
+ expect(state.selectedDate.getDate()).toBe(15);
49
+ expect(state.selectedDate.getMonth()).toBe(0);
50
+ }
51
+ });
52
+
53
+ it('should format dates using local timezone in calendar cells', () => {
54
+ const datepicker = new KTDatepicker(container);
55
+ const testDate = new Date(2024, 0, 15);
56
+ datepicker.setDate(testDate);
57
+ datepicker.open();
58
+
59
+ // Wait for dropdown to render
60
+ const dropdown = container.querySelector('[data-kt-datepicker-dropdown]') as HTMLElement;
61
+ if (!dropdown) {
62
+ // If dropdown not found, skip this test (may need async rendering)
63
+ return;
64
+ }
65
+
66
+ // Find the selected date cell
67
+ const selectedCell = dropdown.querySelector('[data-kt-selected="true"]') as HTMLElement;
68
+ if (selectedCell) {
69
+ // Check that the data-date attribute uses local timezone format
70
+ const dateAttr = selectedCell.getAttribute('data-date');
71
+ expect(dateAttr).toBe(formatDateToLocalString(testDate));
72
+ }
73
+ });
74
+ });
75
+
76
+ describe('Date Range Selection with Local Timezone', () => {
77
+ it('should store range dates in local timezone', () => {
78
+ const datepicker = new KTDatepicker(container, { range: true });
79
+ const startDate = new Date(2024, 0, 10, 10, 0, 0);
80
+ const endDate = new Date(2024, 0, 15, 20, 0, 0);
81
+
82
+ datepicker.setDate(startDate);
83
+ datepicker.setDate(endDate);
84
+
85
+ const state = datepicker.getState();
86
+ expect(state.selectedRange).toBeDefined();
87
+ if (state.selectedRange) {
88
+ expect(state.selectedRange.start).toBeDefined();
89
+ expect(state.selectedRange.end).toBeDefined();
90
+ if (state.selectedRange.start && state.selectedRange.end) {
91
+ expect(state.selectedRange.start.getDate()).toBe(10);
92
+ expect(state.selectedRange.end.getDate()).toBe(15);
93
+ }
94
+ }
95
+ });
96
+
97
+ it('should format range dates using local timezone in data attributes', () => {
98
+ const datepicker = new KTDatepicker(container, { range: true });
99
+ const startDate = new Date(2024, 0, 10);
100
+ const endDate = new Date(2024, 0, 15);
101
+
102
+ datepicker.setDate(startDate);
103
+ datepicker.setDate(endDate);
104
+ datepicker.open();
105
+
106
+ const dropdown = container.querySelector('[data-kt-datepicker-dropdown]') as HTMLElement;
107
+ if (!dropdown) return;
108
+
109
+ const calendar = dropdown.querySelector('table') as HTMLElement;
110
+ if (calendar) {
111
+ // Check range attributes use local timezone format
112
+ const rangeStart = calendar.getAttribute('data-kt-range-start');
113
+ const rangeEnd = calendar.getAttribute('data-kt-range-end');
114
+
115
+ if (rangeStart && rangeEnd) {
116
+ expect(rangeStart).toBe(formatDateToLocalString(startDate));
117
+ expect(rangeEnd).toBe(formatDateToLocalString(endDate));
118
+ }
119
+ }
120
+ });
121
+
122
+ it('should parse range dates from attributes using local timezone', () => {
123
+ const datepicker = new KTDatepicker(container, { range: true });
124
+ const startDate = new Date(2024, 0, 10);
125
+ const endDate = new Date(2024, 0, 15);
126
+
127
+ datepicker.setDate(startDate);
128
+ datepicker.setDate(endDate);
129
+ datepicker.open();
130
+
131
+ const dropdown = container.querySelector('[data-kt-datepicker-dropdown]') as HTMLElement;
132
+ if (!dropdown) return;
133
+
134
+ const calendar = dropdown.querySelector('table') as HTMLElement;
135
+ if (calendar) {
136
+ // Get attributes and parse them
137
+ const rangeStartAttr = calendar.getAttribute('data-kt-range-start');
138
+ const rangeEndAttr = calendar.getAttribute('data-kt-range-end');
139
+
140
+ if (rangeStartAttr && rangeEndAttr) {
141
+ const parsedStart = parseLocalDate(rangeStartAttr);
142
+ const parsedEnd = parseLocalDate(rangeEndAttr);
143
+
144
+ // Parsed dates should match original dates
145
+ expect(parsedStart.getDate()).toBe(startDate.getDate());
146
+ expect(parsedStart.getMonth()).toBe(startDate.getMonth());
147
+ expect(parsedEnd.getDate()).toBe(endDate.getDate());
148
+ expect(parsedEnd.getMonth()).toBe(endDate.getMonth());
149
+ }
150
+ }
151
+ });
152
+ });
153
+
154
+ describe('Date Comparison with Local Timezone', () => {
155
+ it('should compare dates using local timezone normalization', () => {
156
+ const datepicker = new KTDatepicker(container);
157
+ const date1 = new Date(2024, 0, 15, 10, 0, 0);
158
+ const date2 = new Date(2024, 0, 15, 20, 0, 0);
159
+
160
+ datepicker.setDate(date1);
161
+ const state1 = datepicker.getState();
162
+
163
+ datepicker.setDate(date2);
164
+ const state2 = datepicker.getState();
165
+
166
+ // Both should be considered the same day
167
+ if (state1.selectedDate && state2.selectedDate) {
168
+ expect(state1.selectedDate.getDate()).toBe(state2.selectedDate.getDate());
169
+ expect(state1.selectedDate.getMonth()).toBe(state2.selectedDate.getMonth());
170
+ }
171
+ });
172
+
173
+ it('should highlight correct date cell regardless of time component', () => {
174
+ const datepicker = new KTDatepicker(container);
175
+ const testDate = new Date(2024, 0, 15, 23, 59, 59); // Late in the day
176
+
177
+ datepicker.setDate(testDate);
178
+ datepicker.open();
179
+
180
+ const dropdown = container.querySelector('[data-kt-datepicker-dropdown]') as HTMLElement;
181
+ if (dropdown) {
182
+ const selectedCell = dropdown.querySelector('[data-kt-selected="true"]') as HTMLElement;
183
+ if (selectedCell) {
184
+ const dateAttr = selectedCell.getAttribute('data-date');
185
+ expect(dateAttr).toBe('2024-01-15'); // Should be Jan 15, not shifted
186
+ }
187
+ }
188
+ });
189
+ });
190
+
191
+ describe('Date String Formatting Consistency', () => {
192
+ it('should use consistent local timezone format throughout', () => {
193
+ const datepicker = new KTDatepicker(container);
194
+ const testDate = new Date(2024, 0, 15, 14, 30, 0);
195
+
196
+ datepicker.setDate(testDate);
197
+ datepicker.open();
198
+
199
+ const dropdown = container.querySelector('[data-kt-datepicker-dropdown]') as HTMLElement;
200
+ if (dropdown) {
201
+ const calendar = dropdown.querySelector('table') as HTMLElement;
202
+ if (calendar) {
203
+ // All date cells should use local timezone format
204
+ const dateCells = calendar.querySelectorAll('[data-date]');
205
+ dateCells.forEach(cell => {
206
+ const dateStr = cell.getAttribute('data-date');
207
+ if (dateStr) {
208
+ // Should be in YYYY-MM-DD format
209
+ expect(dateStr).toMatch(/^\d{4}-\d{2}-\d{2}$/);
210
+ // Should be parseable as local date
211
+ const parsed = parseLocalDate(dateStr);
212
+ expect(parsed).toBeInstanceOf(Date);
213
+ }
214
+ });
215
+ }
216
+ }
217
+ });
218
+ });
219
+ });
220
+