kempo-ui 0.3.7 → 0.3.8

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 (266) hide show
  1. package/dist/components/Accordion.js +5 -5
  2. package/dist/components/Aside.js +16 -16
  3. package/dist/components/Card.js +3 -3
  4. package/dist/components/ColorPicker.js +4 -4
  5. package/dist/components/ContentSlider.js +3 -3
  6. package/dist/components/Context.js +1 -1
  7. package/dist/components/Dialog.js +7 -7
  8. package/dist/components/Dropdown.js +7 -4
  9. package/dist/components/FilterItem.js +2 -2
  10. package/dist/components/FilterList.js +2 -2
  11. package/dist/components/FocusCapture.js +2 -2
  12. package/dist/components/HtmlEditor.js +62 -39
  13. package/dist/components/HybridComponent.js +1 -1
  14. package/dist/components/Icon.js +2 -2
  15. package/dist/components/Import.js +1 -1
  16. package/dist/components/LightComponent.js +1 -1
  17. package/dist/components/Main.js +3 -3
  18. package/dist/components/Nav.js +2 -2
  19. package/dist/components/NavSpacer.js +6 -0
  20. package/dist/components/PhotoViewer.js +3 -3
  21. package/dist/components/Resize.js +3 -3
  22. package/dist/components/ShadowComponent.js +1 -1
  23. package/dist/components/ShowMore.js +3 -3
  24. package/dist/components/Sortable.js +3 -3
  25. package/dist/components/Spinner.js +2 -2
  26. package/dist/components/Split.js +3 -3
  27. package/dist/components/Table.js +27 -27
  28. package/dist/components/Tabs.js +7 -7
  29. package/dist/components/Tags.js +4 -4
  30. package/dist/components/ThemeSelect.js +4 -4
  31. package/dist/components/ThemeSwitcher.js +5 -5
  32. package/dist/components/Timestamp.js +1 -1
  33. package/dist/components/Toast.js +3 -3
  34. package/dist/components/Toggle.js +3 -3
  35. package/dist/components/Tree.js +10 -10
  36. package/dist/components/htmlEditorControls/AlignCenter.js +4 -4
  37. package/dist/components/htmlEditorControls/AlignJustify.js +4 -4
  38. package/dist/components/htmlEditorControls/AlignLeft.js +4 -4
  39. package/dist/components/htmlEditorControls/AlignRight.js +4 -4
  40. package/dist/components/htmlEditorControls/Bold.js +4 -4
  41. package/dist/components/htmlEditorControls/BulletList.js +4 -4
  42. package/dist/components/htmlEditorControls/CharacterCount.js +3 -3
  43. package/dist/components/htmlEditorControls/ClearFormatting.js +4 -4
  44. package/dist/components/htmlEditorControls/CodeBlock.js +3 -3
  45. package/dist/components/htmlEditorControls/ControlGroup.js +2 -2
  46. package/dist/components/htmlEditorControls/ControlSpacer.js +2 -2
  47. package/dist/components/htmlEditorControls/CreateLink.js +3 -3
  48. package/dist/components/htmlEditorControls/DropdownControl.js +3 -3
  49. package/dist/components/htmlEditorControls/FormatBlock.js +4 -4
  50. package/dist/components/htmlEditorControls/HtmlEditorControl.js +2 -2
  51. package/dist/components/htmlEditorControls/InlineCode.js +3 -3
  52. package/dist/components/htmlEditorControls/InsertTable.js +3 -3
  53. package/dist/components/htmlEditorControls/Italic.js +4 -4
  54. package/dist/components/htmlEditorControls/Mode.js +3 -3
  55. package/dist/components/htmlEditorControls/NumberList.js +4 -4
  56. package/dist/components/htmlEditorControls/Strikethrough.js +4 -4
  57. package/dist/components/htmlEditorControls/TextBackgroundColor.js +6 -6
  58. package/dist/components/htmlEditorControls/TextColor.js +7 -7
  59. package/dist/components/htmlEditorControls/Underline.js +4 -4
  60. package/dist/components/htmlEditorControls/WordCount.js +3 -3
  61. package/dist/components/htmlEditorNodes/HtmlComment.js +24 -0
  62. package/dist/components/tableControls/DeleteRecord.js +2 -2
  63. package/dist/components/tableControls/DeleteSelected.js +2 -2
  64. package/dist/components/tableControls/Edit.js +4 -4
  65. package/dist/components/tableControls/ExportCSV.js +2 -2
  66. package/dist/components/tableControls/ExportJson.js +2 -2
  67. package/dist/components/tableControls/FieldSortHide.js +4 -4
  68. package/dist/components/tableControls/Filters.js +17 -17
  69. package/dist/components/tableControls/FirstPage.js +2 -2
  70. package/dist/components/tableControls/HiddenCount.js +2 -2
  71. package/dist/components/tableControls/Hide.js +2 -2
  72. package/dist/components/tableControls/LastPage.js +2 -2
  73. package/dist/components/tableControls/NextPage.js +2 -2
  74. package/dist/components/tableControls/PageSelect.js +4 -4
  75. package/dist/components/tableControls/PageSize.js +3 -3
  76. package/dist/components/tableControls/PrevPage.js +2 -2
  77. package/dist/components/tableControls/Search.js +2 -2
  78. package/dist/components/tableControls/ShowAll.js +2 -2
  79. package/dist/components/tableControls/TableControl.js +2 -2
  80. package/dist/utils/context.js +1 -1
  81. package/dist/utils/debounce.js +1 -1
  82. package/dist/utils/object.js +1 -1
  83. package/dist/utils/theme.js +1 -1
  84. package/dist/utils/wait.js +0 -1
  85. package/docs/components/accordion.html +2 -0
  86. package/docs/components/aside.html +9 -8
  87. package/docs/components/card.html +2 -0
  88. package/docs/components/color-picker.html +2 -0
  89. package/docs/components/content-slider.html +2 -0
  90. package/docs/components/context.html +2 -0
  91. package/docs/components/dialog.html +2 -0
  92. package/docs/components/dropdown.html +2 -0
  93. package/docs/components/filter-list.html +2 -0
  94. package/docs/components/focus-capture.html +2 -0
  95. package/docs/components/html-editor.html +54 -6
  96. package/docs/components/hybrid-component.html +2 -0
  97. package/docs/components/icon.html +2 -0
  98. package/docs/components/import.html +2 -0
  99. package/docs/components/light-component.html +2 -0
  100. package/docs/components/nav-spacer.html +315 -0
  101. package/docs/components/nav.html +2 -0
  102. package/docs/components/photo-viewer.html +2 -0
  103. package/docs/components/resize.html +2 -0
  104. package/docs/components/shadow-component.html +2 -0
  105. package/docs/components/show-more.html +2 -0
  106. package/docs/components/sortable.html +2 -0
  107. package/docs/components/spinner.html +2 -0
  108. package/docs/components/split.html +2 -0
  109. package/docs/components/table.html +2 -0
  110. package/docs/components/tableControls.html +2 -0
  111. package/docs/components/tableCustomFields.html +2 -0
  112. package/docs/components/tableFetchRecords.html +2 -0
  113. package/docs/components/tableFieldSortHide.html +2 -0
  114. package/docs/components/tablePagination.html +2 -0
  115. package/docs/components/tablePlaceholder.html +2 -0
  116. package/docs/components/tableRecordEditing.html +2 -0
  117. package/docs/components/tableRecordFiltering.html +2 -0
  118. package/docs/components/tableRecordHiding.html +2 -0
  119. package/docs/components/tableRecordSearching.html +2 -0
  120. package/docs/components/tableRecordSelection.html +2 -0
  121. package/docs/components/tableRowControls.html +2 -0
  122. package/docs/components/tableServerSync.html +2 -0
  123. package/docs/components/tableSorting.html +2 -0
  124. package/docs/components/tabs.html +2 -0
  125. package/docs/components/tags.html +2 -0
  126. package/docs/components/theme-select.html +2 -0
  127. package/docs/components/theme-switcher.html +2 -0
  128. package/docs/components/timestamp.html +2 -0
  129. package/docs/components/toast.html +2 -0
  130. package/docs/components/toggle.html +2 -0
  131. package/docs/components/tree.html +2 -0
  132. package/docs/icons/comment.svg +1 -0
  133. package/docs/index.html +8 -0
  134. package/docs/src/components/Accordion.js +5 -5
  135. package/docs/src/components/Aside.js +16 -16
  136. package/docs/src/components/Card.js +3 -3
  137. package/docs/src/components/ColorPicker.js +4 -4
  138. package/docs/src/components/ContentSlider.js +3 -3
  139. package/docs/src/components/Context.js +1 -1
  140. package/docs/src/components/Dialog.js +7 -7
  141. package/docs/src/components/Dropdown.js +7 -4
  142. package/docs/src/components/FilterItem.js +2 -2
  143. package/docs/src/components/FilterList.js +2 -2
  144. package/docs/src/components/FocusCapture.js +2 -2
  145. package/docs/src/components/HtmlEditor.js +62 -39
  146. package/docs/src/components/HybridComponent.js +1 -1
  147. package/docs/src/components/Icon.js +2 -2
  148. package/docs/src/components/Import.js +1 -1
  149. package/docs/src/components/LightComponent.js +1 -1
  150. package/docs/src/components/Main.js +3 -3
  151. package/docs/src/components/Nav.js +2 -2
  152. package/docs/src/components/NavSpacer.js +6 -0
  153. package/docs/src/components/PhotoViewer.js +3 -3
  154. package/docs/src/components/Resize.js +3 -3
  155. package/docs/src/components/ShadowComponent.js +1 -1
  156. package/docs/src/components/ShowMore.js +3 -3
  157. package/docs/src/components/Sortable.js +3 -3
  158. package/docs/src/components/Spinner.js +2 -2
  159. package/docs/src/components/Split.js +3 -3
  160. package/docs/src/components/Table.js +27 -27
  161. package/docs/src/components/Tabs.js +7 -7
  162. package/docs/src/components/Tags.js +4 -4
  163. package/docs/src/components/ThemeSelect.js +4 -4
  164. package/docs/src/components/ThemeSwitcher.js +5 -5
  165. package/docs/src/components/Timestamp.js +1 -1
  166. package/docs/src/components/Toast.js +3 -3
  167. package/docs/src/components/Toggle.js +3 -3
  168. package/docs/src/components/Tree.js +10 -10
  169. package/docs/src/components/htmlEditorControls/AlignCenter.js +4 -4
  170. package/docs/src/components/htmlEditorControls/AlignJustify.js +4 -4
  171. package/docs/src/components/htmlEditorControls/AlignLeft.js +4 -4
  172. package/docs/src/components/htmlEditorControls/AlignRight.js +4 -4
  173. package/docs/src/components/htmlEditorControls/Bold.js +4 -4
  174. package/docs/src/components/htmlEditorControls/BulletList.js +4 -4
  175. package/docs/src/components/htmlEditorControls/CharacterCount.js +3 -3
  176. package/docs/src/components/htmlEditorControls/ClearFormatting.js +4 -4
  177. package/docs/src/components/htmlEditorControls/CodeBlock.js +3 -3
  178. package/docs/src/components/htmlEditorControls/ControlGroup.js +2 -2
  179. package/docs/src/components/htmlEditorControls/ControlSpacer.js +2 -2
  180. package/docs/src/components/htmlEditorControls/CreateLink.js +3 -3
  181. package/docs/src/components/htmlEditorControls/DropdownControl.js +3 -3
  182. package/docs/src/components/htmlEditorControls/FormatBlock.js +4 -4
  183. package/docs/src/components/htmlEditorControls/HtmlEditorControl.js +2 -2
  184. package/docs/src/components/htmlEditorControls/InlineCode.js +3 -3
  185. package/docs/src/components/htmlEditorControls/InsertTable.js +3 -3
  186. package/docs/src/components/htmlEditorControls/Italic.js +4 -4
  187. package/docs/src/components/htmlEditorControls/Mode.js +3 -3
  188. package/docs/src/components/htmlEditorControls/NumberList.js +4 -4
  189. package/docs/src/components/htmlEditorControls/Strikethrough.js +4 -4
  190. package/docs/src/components/htmlEditorControls/TextBackgroundColor.js +6 -6
  191. package/docs/src/components/htmlEditorControls/TextColor.js +7 -7
  192. package/docs/src/components/htmlEditorControls/Underline.js +4 -4
  193. package/docs/src/components/htmlEditorControls/WordCount.js +3 -3
  194. package/docs/src/components/htmlEditorNodes/HtmlComment.js +24 -0
  195. package/docs/src/components/tableControls/DeleteRecord.js +2 -2
  196. package/docs/src/components/tableControls/DeleteSelected.js +2 -2
  197. package/docs/src/components/tableControls/Edit.js +4 -4
  198. package/docs/src/components/tableControls/ExportCSV.js +2 -2
  199. package/docs/src/components/tableControls/ExportJson.js +2 -2
  200. package/docs/src/components/tableControls/FieldSortHide.js +4 -4
  201. package/docs/src/components/tableControls/Filters.js +17 -17
  202. package/docs/src/components/tableControls/FirstPage.js +2 -2
  203. package/docs/src/components/tableControls/HiddenCount.js +2 -2
  204. package/docs/src/components/tableControls/Hide.js +2 -2
  205. package/docs/src/components/tableControls/LastPage.js +2 -2
  206. package/docs/src/components/tableControls/NextPage.js +2 -2
  207. package/docs/src/components/tableControls/PageSelect.js +4 -4
  208. package/docs/src/components/tableControls/PageSize.js +3 -3
  209. package/docs/src/components/tableControls/PrevPage.js +2 -2
  210. package/docs/src/components/tableControls/Search.js +2 -2
  211. package/docs/src/components/tableControls/ShowAll.js +2 -2
  212. package/docs/src/components/tableControls/TableControl.js +2 -2
  213. package/docs/src/utils/context.js +1 -1
  214. package/docs/src/utils/debounce.js +1 -1
  215. package/docs/src/utils/object.js +1 -1
  216. package/docs/src/utils/theme.js +1 -1
  217. package/docs/src/utils/wait.js +0 -1
  218. package/docs/utils/context.html +2 -0
  219. package/docs/utils/cookie.html +2 -0
  220. package/docs/utils/debounce.html +2 -0
  221. package/docs/utils/drag.html +2 -0
  222. package/docs/utils/elevation.html +2 -0
  223. package/docs/utils/formatTimestamp.html +2 -0
  224. package/docs/utils/object.html +2 -0
  225. package/docs/utils/propConverters.html +2 -0
  226. package/docs/utils/string.html +2 -0
  227. package/docs/utils/theme.html +2 -0
  228. package/docs/utils/toTitleCase.html +2 -0
  229. package/docs/utils/type.html +2 -0
  230. package/docs/utils/wait.html +2 -0
  231. package/docs-src/components/aside.page.html +7 -8
  232. package/docs-src/components/html-editor.page.html +52 -6
  233. package/docs-src/components/nav-spacer.page.html +65 -0
  234. package/docs-src/index.page.html +6 -0
  235. package/docs-src/nav.fragment.html +2 -0
  236. package/icons/comment.svg +1 -0
  237. package/llm.txt +1 -0
  238. package/package.json +1 -1
  239. package/scripts/build.js +1 -1
  240. package/src/components/Accordion.js +1 -1
  241. package/src/components/Dropdown.js +7 -3
  242. package/src/components/HtmlEditor.js +699 -1206
  243. package/src/components/NavSpacer.js +55 -0
  244. package/src/components/htmlEditorControls/AlignCenter.js +5 -5
  245. package/src/components/htmlEditorControls/AlignJustify.js +5 -5
  246. package/src/components/htmlEditorControls/AlignLeft.js +5 -5
  247. package/src/components/htmlEditorControls/AlignRight.js +5 -5
  248. package/src/components/htmlEditorControls/Bold.js +5 -5
  249. package/src/components/htmlEditorControls/BulletList.js +5 -5
  250. package/src/components/htmlEditorControls/ClearFormatting.js +5 -5
  251. package/src/components/htmlEditorControls/CodeBlock.js +16 -8
  252. package/src/components/htmlEditorControls/CreateLink.js +3 -7
  253. package/src/components/htmlEditorControls/FormatBlock.js +5 -5
  254. package/src/components/htmlEditorControls/InlineCode.js +1 -13
  255. package/src/components/htmlEditorControls/InsertTable.js +43 -44
  256. package/src/components/htmlEditorControls/Italic.js +5 -5
  257. package/src/components/htmlEditorControls/Mode.js +1 -1
  258. package/src/components/htmlEditorControls/NumberList.js +5 -5
  259. package/src/components/htmlEditorControls/Strikethrough.js +5 -5
  260. package/src/components/htmlEditorControls/Underline.js +5 -5
  261. package/src/components/htmlEditorNodes/HtmlComment.js +118 -0
  262. package/tests/components/Accordion.browser-test.js +154 -0
  263. package/tests/components/Dropdown.browser-test.js +85 -0
  264. package/tests/components/HtmlComment.browser-test.js +115 -0
  265. package/tests/components/HtmlEditor.browser-test.js +379 -0
  266. package/tests/components/NavSpacer.browser-test.js +81 -0
@@ -0,0 +1,379 @@
1
+ import HtmlEditor from '../../src/components/HtmlEditor.js';
2
+
3
+ const wait = ms => new Promise(r => setTimeout(r, ms));
4
+
5
+ const createEditor = async (options = {}) => {
6
+ const container = document.createElement('div');
7
+ container.innerHTML = `
8
+ <k-html-editor
9
+ ${options.value ? `value="${options.value.replace(/"/g, '&quot;')}"` : ''}
10
+ ${options.mode ? `mode="${options.mode}"` : ''}
11
+ ${options.nodes ? `nodes="${options.nodes}"` : ''}
12
+ ></k-html-editor>
13
+ `;
14
+ document.body.appendChild(container);
15
+ const editor = container.querySelector('k-html-editor');
16
+ await new Promise(r => editor.addEventListener('ready', r, { once: true }));
17
+ return { container, editor };
18
+ };
19
+
20
+ const cleanup = container => {
21
+ if(container?.parentNode) container.parentNode.removeChild(container);
22
+ };
23
+
24
+ export default {
25
+ /*
26
+ Element Creation
27
+ */
28
+ 'should create html editor element': async ({pass, fail}) => {
29
+ const { container, editor } = await createEditor();
30
+ if(!(editor instanceof HtmlEditor)) {
31
+ cleanup(container);
32
+ return fail('Element should be instance of HtmlEditor');
33
+ }
34
+ cleanup(container);
35
+ pass('HtmlEditor element created correctly');
36
+ },
37
+
38
+ 'should have shadow root': async ({pass, fail}) => {
39
+ const { container, editor } = await createEditor();
40
+ if(!editor.shadowRoot) {
41
+ cleanup(container);
42
+ return fail('HtmlEditor should have shadow root');
43
+ }
44
+ cleanup(container);
45
+ pass('HtmlEditor has shadow root');
46
+ },
47
+
48
+ 'should default to visual mode': async ({pass, fail}) => {
49
+ const { container, editor } = await createEditor();
50
+ if(editor.mode !== 'visual') {
51
+ cleanup(container);
52
+ return fail(`Expected mode 'visual', got '${editor.mode}'`);
53
+ }
54
+ cleanup(container);
55
+ pass('Default mode is visual');
56
+ },
57
+
58
+ /*
59
+ Compatibility Detection - Compatible Content
60
+ */
61
+ 'isVisualCompatible should return true for simple HTML': async ({pass, fail}) => {
62
+ const { container, editor } = await createEditor();
63
+ if(!editor.isVisualCompatible('<p>Hello</p>')) {
64
+ cleanup(container);
65
+ return fail('Simple HTML should be compatible');
66
+ }
67
+ cleanup(container);
68
+ pass('Simple HTML is compatible');
69
+ },
70
+
71
+ 'isVisualCompatible should return true for empty string': async ({pass, fail}) => {
72
+ const { container, editor } = await createEditor();
73
+ if(!editor.isVisualCompatible('')) {
74
+ cleanup(container);
75
+ return fail('Empty string should be compatible');
76
+ }
77
+ cleanup(container);
78
+ pass('Empty string is compatible');
79
+ },
80
+
81
+ 'isVisualCompatible should return true for standard tags': async ({pass, fail}) => {
82
+ const { container, editor } = await createEditor();
83
+ const html = '<h1>Title</h1><p>Text</p><ul><li>Item</li></ul><a href="#">Link</a><strong>Bold</strong>';
84
+ if(!editor.isVisualCompatible(html)) {
85
+ cleanup(container);
86
+ return fail('Standard HTML tags should be compatible');
87
+ }
88
+ cleanup(container);
89
+ pass('Standard HTML tags are compatible');
90
+ },
91
+
92
+ /*
93
+ Compatibility Detection - Incompatible Content
94
+ */
95
+ 'isVisualCompatible should return false for svg': async ({pass, fail}) => {
96
+ const { container, editor } = await createEditor();
97
+ if(editor.isVisualCompatible('<p>Text</p><svg><rect/></svg>')) {
98
+ cleanup(container);
99
+ return fail('SVG should be incompatible');
100
+ }
101
+ cleanup(container);
102
+ pass('SVG is incompatible');
103
+ },
104
+
105
+ 'isVisualCompatible should return false for script in body context': async ({pass, fail}) => {
106
+ const { container, editor } = await createEditor();
107
+ if(editor.isVisualCompatible('<p>Text</p><script>alert(1)</script><p>More</p>')) {
108
+ cleanup(container);
109
+ return fail('Script should be incompatible');
110
+ }
111
+ cleanup(container);
112
+ pass('Script is incompatible');
113
+ },
114
+
115
+ 'isVisualCompatible should return false for style in body context': async ({pass, fail}) => {
116
+ const { container, editor } = await createEditor();
117
+ if(editor.isVisualCompatible('<p>Text</p><style>body{}</style><p>More</p>')) {
118
+ cleanup(container);
119
+ return fail('Style should be incompatible');
120
+ }
121
+ cleanup(container);
122
+ pass('Style is incompatible');
123
+ },
124
+
125
+ 'isVisualCompatible should return false for form elements': async ({pass, fail}) => {
126
+ const { container, editor } = await createEditor();
127
+ const tags = ['form', 'input', 'textarea', 'select', 'button'];
128
+ for(const tag of tags) {
129
+ if(editor.isVisualCompatible(`<${tag}></${tag}>`)) {
130
+ cleanup(container);
131
+ return fail(`<${tag}> should be incompatible`);
132
+ }
133
+ }
134
+ cleanup(container);
135
+ pass('Form elements are incompatible');
136
+ },
137
+
138
+ 'isVisualCompatible should return false for HTML comments without custom node': async ({pass, fail}) => {
139
+ const { container, editor } = await createEditor();
140
+ if(editor.isVisualCompatible('<p>Text <!-- comment --> more</p>')) {
141
+ cleanup(container);
142
+ return fail('HTML comments should be incompatible without HtmlComment node');
143
+ }
144
+ cleanup(container);
145
+ pass('HTML comments are incompatible without custom node');
146
+ },
147
+
148
+ /*
149
+ Auto Mode Detection
150
+ */
151
+ 'should auto-switch to code mode for incompatible initial value': async ({pass, fail}) => {
152
+ const { container, editor } = await createEditor({
153
+ value: '<p>Has SVG</p><svg><rect/></svg>'
154
+ });
155
+ if(editor.mode !== 'code') {
156
+ cleanup(container);
157
+ return fail(`Expected mode 'code' for incompatible content, got '${editor.mode}'`);
158
+ }
159
+ cleanup(container);
160
+ pass('Auto-switches to code mode for incompatible content');
161
+ },
162
+
163
+ 'should stay in visual mode for compatible initial value': async ({pass, fail}) => {
164
+ const { container, editor } = await createEditor({
165
+ value: '<p>Simple paragraph</p>'
166
+ });
167
+ if(editor.mode !== 'visual') {
168
+ cleanup(container);
169
+ return fail(`Expected mode 'visual' for compatible content, got '${editor.mode}'`);
170
+ }
171
+ cleanup(container);
172
+ pass('Stays in visual mode for compatible content');
173
+ },
174
+
175
+ /*
176
+ Node Compatibility Hooks
177
+ */
178
+ 'should collect nodeCompatCheckers from custom nodes': async ({pass, fail}) => {
179
+ const { container, editor } = await createEditor();
180
+ if(!Array.isArray(editor.nodeCompatCheckers)) {
181
+ cleanup(container);
182
+ return fail('nodeCompatCheckers should be an array');
183
+ }
184
+ cleanup(container);
185
+ pass('nodeCompatCheckers is initialized as array');
186
+ },
187
+
188
+ 'should collect nodePreprocessors from custom nodes': async ({pass, fail}) => {
189
+ const { container, editor } = await createEditor();
190
+ if(!Array.isArray(editor.nodePreprocessors)) {
191
+ cleanup(container);
192
+ return fail('nodePreprocessors should be an array');
193
+ }
194
+ cleanup(container);
195
+ pass('nodePreprocessors is initialized as array');
196
+ },
197
+
198
+ /*
199
+ Public Methods - Mode Control
200
+ */
201
+ 'setMode should accept visual': async ({pass, fail}) => {
202
+ const { container, editor } = await createEditor();
203
+ editor.setMode('visual');
204
+ if(editor.mode !== 'visual') {
205
+ cleanup(container);
206
+ return fail(`Expected mode 'visual', got '${editor.mode}'`);
207
+ }
208
+ cleanup(container);
209
+ pass('setMode accepts visual');
210
+ },
211
+
212
+ 'setMode should reject invalid modes': async ({pass, fail}) => {
213
+ const { container, editor } = await createEditor();
214
+ editor.setMode('invalid');
215
+ if(editor.mode !== 'visual') {
216
+ cleanup(container);
217
+ return fail(`Mode should not change for invalid value, got '${editor.mode}'`);
218
+ }
219
+ cleanup(container);
220
+ pass('setMode rejects invalid modes');
221
+ },
222
+
223
+ 'setMode should return this for chaining': async ({pass, fail}) => {
224
+ const { container, editor } = await createEditor();
225
+ const result = editor.setMode('visual');
226
+ if(result !== editor) {
227
+ cleanup(container);
228
+ return fail('setMode should return this');
229
+ }
230
+ cleanup(container);
231
+ pass('setMode returns this');
232
+ },
233
+
234
+ 'toggleMode should switch from visual to code': async ({pass, fail}) => {
235
+ const { container, editor } = await createEditor();
236
+ editor.toggleMode();
237
+ await wait(500);
238
+ if(editor.mode !== 'code') {
239
+ cleanup(container);
240
+ return fail(`Expected mode 'code' after toggle, got '${editor.mode}'`);
241
+ }
242
+ cleanup(container);
243
+ pass('toggleMode switches to code');
244
+ },
245
+
246
+ /*
247
+ Public Methods - Content Management
248
+ */
249
+ 'getValue should return current content': async ({pass, fail}) => {
250
+ const { container, editor } = await createEditor({ value: '<p>Test content</p>' });
251
+ const val = editor.getValue();
252
+ if(!val.includes('Test content')) {
253
+ cleanup(container);
254
+ return fail(`getValue should contain 'Test content', got '${val}'`);
255
+ }
256
+ cleanup(container);
257
+ pass('getValue returns current content');
258
+ },
259
+
260
+ 'setValue should update content': async ({pass, fail}) => {
261
+ const { container, editor } = await createEditor();
262
+ editor.setValue('<p>New content</p>');
263
+ const val = editor.getValue();
264
+ if(!val.includes('New content')) {
265
+ cleanup(container);
266
+ return fail(`setValue should update content, got '${val}'`);
267
+ }
268
+ cleanup(container);
269
+ pass('setValue updates content');
270
+ },
271
+
272
+ 'setValue should auto-switch to code for incompatible content': async ({pass, fail}) => {
273
+ const { container, editor } = await createEditor();
274
+ editor.setValue('<p>Text</p><svg><rect/></svg>');
275
+ await wait(300);
276
+ if(editor.mode !== 'code') {
277
+ cleanup(container);
278
+ return fail(`Expected mode 'code' after setting incompatible content, got '${editor.mode}'`);
279
+ }
280
+ cleanup(container);
281
+ pass('setValue auto-switches to code for incompatible content');
282
+ },
283
+
284
+ 'setValue should return this for chaining': async ({pass, fail}) => {
285
+ const { container, editor } = await createEditor();
286
+ const result = editor.setValue('<p>Test</p>');
287
+ if(result !== editor) {
288
+ cleanup(container);
289
+ return fail('setValue should return this');
290
+ }
291
+ cleanup(container);
292
+ pass('setValue returns this');
293
+ },
294
+
295
+ 'clear should empty the editor': async ({pass, fail}) => {
296
+ const { container, editor } = await createEditor({ value: '<p>Some content</p>' });
297
+ editor.clear();
298
+ const val = editor.getValue();
299
+ if(val.includes('Some content')) {
300
+ cleanup(container);
301
+ return fail('clear should remove content');
302
+ }
303
+ cleanup(container);
304
+ pass('clear empties the editor');
305
+ },
306
+
307
+ /*
308
+ Events
309
+ */
310
+ 'should dispatch ready event': async ({pass, fail}) => {
311
+ const container = document.createElement('div');
312
+ container.innerHTML = '<k-html-editor value="<p>Test</p>"></k-html-editor>';
313
+ const editor = container.querySelector('k-html-editor');
314
+ let fired = false;
315
+ editor.addEventListener('ready', () => { fired = true; }, { once: true });
316
+ document.body.appendChild(container);
317
+ await new Promise(r => setTimeout(r, 5000));
318
+ if(!fired) {
319
+ cleanup(container);
320
+ return fail('ready event should fire');
321
+ }
322
+ cleanup(container);
323
+ pass('ready event fires');
324
+ },
325
+
326
+ 'should dispatch mode-changed event': async ({pass, fail}) => {
327
+ const { container, editor } = await createEditor();
328
+ let firedMode = null;
329
+ editor.addEventListener('mode-changed', e => { firedMode = e.detail.mode; }, { once: true });
330
+ editor.setMode('code');
331
+ await wait(500);
332
+ if(firedMode !== 'code') {
333
+ cleanup(container);
334
+ return fail(`Expected mode-changed with 'code', got '${firedMode}'`);
335
+ }
336
+ cleanup(container);
337
+ pass('mode-changed event fires');
338
+ },
339
+
340
+ /*
341
+ HTML Export Cleanup
342
+ */
343
+ 'cleanExportedHtml should strip k-editor classes': async ({pass, fail}) => {
344
+ const { container, editor } = await createEditor();
345
+ const cleaned = editor.cleanExportedHtml('<p class="k-editor-p">Text</p>');
346
+ if(cleaned.includes('k-editor-p')) {
347
+ cleanup(container);
348
+ return fail('k-editor classes should be stripped');
349
+ }
350
+ cleanup(container);
351
+ pass('k-editor classes are stripped');
352
+ },
353
+
354
+ 'cleanExportedHtml should strip pre-wrap white-space': async ({pass, fail}) => {
355
+ const { container, editor } = await createEditor();
356
+ const cleaned = editor.cleanExportedHtml('<p style="white-space: pre-wrap;">Text</p>');
357
+ if(cleaned.includes('white-space')) {
358
+ cleanup(container);
359
+ return fail('white-space: pre-wrap should be stripped');
360
+ }
361
+ cleanup(container);
362
+ pass('white-space: pre-wrap is stripped');
363
+ },
364
+
365
+ 'cleanExportedHtml should unwrap empty spans': async ({pass, fail}) => {
366
+ const { container, editor } = await createEditor();
367
+ const cleaned = editor.cleanExportedHtml('<p><span>Text</span></p>');
368
+ if(cleaned.includes('<span>')) {
369
+ cleanup(container);
370
+ return fail('Empty spans should be unwrapped');
371
+ }
372
+ if(!cleaned.includes('Text')) {
373
+ cleanup(container);
374
+ return fail('Text content should be preserved');
375
+ }
376
+ cleanup(container);
377
+ pass('Empty spans are unwrapped');
378
+ }
379
+ };
@@ -0,0 +1,81 @@
1
+ import NavSpacer from '../../src/components/NavSpacer.js';
2
+ import '../../src/components/Nav.js';
3
+
4
+ const createNavSpacer = async () => {
5
+ const container = document.createElement('div');
6
+ container.innerHTML = `<k-nav-spacer></k-nav-spacer>`;
7
+ document.body.appendChild(container);
8
+ const el = container.querySelector('k-nav-spacer');
9
+ await el.updateComplete;
10
+ return { container, el };
11
+ };
12
+
13
+ const cleanup = (container) => {
14
+ if(container && container.parentNode){
15
+ container.parentNode.removeChild(container);
16
+ }
17
+ };
18
+
19
+ export default {
20
+ /*
21
+ Element Creation
22
+ */
23
+ 'should create nav-spacer element': async ({pass, fail}) => {
24
+ const { container, el } = await createNavSpacer();
25
+ if(!(el instanceof NavSpacer)){
26
+ cleanup(container);
27
+ return fail('Element should be instance of NavSpacer');
28
+ }
29
+ cleanup(container);
30
+ pass('NavSpacer element created correctly');
31
+ },
32
+
33
+ 'should have shadow root': async ({pass, fail}) => {
34
+ const { container, el } = await createNavSpacer();
35
+ if(!el.shadowRoot){
36
+ cleanup(container);
37
+ return fail('NavSpacer should have shadow root');
38
+ }
39
+ cleanup(container);
40
+ pass('NavSpacer has shadow root');
41
+ },
42
+
43
+ /*
44
+ Behaviour
45
+ */
46
+ 'should set height 0 when no fixed nav present': async ({pass, fail}) => {
47
+ const { container, el } = await createNavSpacer();
48
+ const h = el.style.height;
49
+ if(h !== '0px'){
50
+ cleanup(container);
51
+ return fail(`Expected height 0px when no k-nav[fixed] exists, got "${h}"`);
52
+ }
53
+ cleanup(container);
54
+ pass('Height is 0px when no fixed nav present');
55
+ },
56
+
57
+ 'should match height of fixed nav when present': async ({pass, fail}) => {
58
+ const navContainer = document.createElement('div');
59
+ navContainer.innerHTML = `<k-nav fixed style="height:56px"></k-nav>`;
60
+ document.body.appendChild(navContainer);
61
+ await customElements.whenDefined('k-nav');
62
+ const nav = navContainer.querySelector('k-nav');
63
+ await nav.updateComplete;
64
+
65
+ const { container, el } = await createNavSpacer();
66
+
67
+ // Poll until ResizeObserver fires (up to 1s)
68
+ const start = Date.now();
69
+ while(el.style.height === '' && Date.now() - start < 1000){
70
+ await new Promise(r => setTimeout(r, 20));
71
+ }
72
+
73
+ const h = el.style.height;
74
+ cleanup(container);
75
+ cleanup(navContainer);
76
+ if(h === ''){
77
+ return fail('ResizeObserver never fired — height was never set');
78
+ }
79
+ pass(`NavSpacer height was set to ${h}`);
80
+ },
81
+ };