@skewedaspect/sleekspace-ui 0.9.0 → 0.10.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 (210) hide show
  1. package/dist/components/Accordion/context.d.ts +4 -0
  2. package/dist/components/Autocomplete/SkAutocomplete.vue.d.ts +87 -0
  3. package/dist/components/Autocomplete/SkAutocompleteEmpty.vue.d.ts +17 -0
  4. package/dist/components/Autocomplete/SkAutocompleteGroup.vue.d.ts +17 -0
  5. package/dist/components/Autocomplete/SkAutocompleteGroupLabel.vue.d.ts +17 -0
  6. package/dist/components/Autocomplete/SkAutocompleteItem.vue.d.ts +39 -0
  7. package/dist/components/Autocomplete/SkAutocompleteSeparator.vue.d.ts +2 -0
  8. package/dist/components/Autocomplete/index.d.ts +7 -0
  9. package/dist/components/Autocomplete/types.d.ts +3 -0
  10. package/dist/components/Breadcrumbs/context.d.ts +4 -0
  11. package/dist/components/Button/SkButton.vue.d.ts +8 -1
  12. package/dist/components/Button/types.d.ts +2 -0
  13. package/dist/components/Card/SkCard.vue.d.ts +1 -1
  14. package/dist/components/ContextMenu/context.d.ts +3 -0
  15. package/dist/components/Dropdown/SkDropdown.vue.d.ts +1 -1
  16. package/dist/components/Dropdown/context.d.ts +3 -0
  17. package/dist/components/Field/SkField.vue.d.ts +7 -6
  18. package/dist/components/Input/SkInput.vue.d.ts +9 -2
  19. package/dist/components/Input/types.d.ts +2 -0
  20. package/dist/components/InputGroup/SkInputGroup.vue.d.ts +23 -0
  21. package/dist/components/InputGroup/SkInputGroupAddon.vue.d.ts +33 -0
  22. package/dist/components/InputGroup/types.d.ts +13 -0
  23. package/dist/components/NumberInput/SkNumberInput.vue.d.ts +15 -1
  24. package/dist/components/NumberInput/types.d.ts +2 -0
  25. package/dist/components/Pagination/context.d.ts +5 -0
  26. package/dist/components/Panel/SkPanel.vue.d.ts +1 -1
  27. package/dist/components/Panel/types.d.ts +2 -1
  28. package/dist/components/Radio/context.d.ts +4 -0
  29. package/dist/components/Select/SkSelect.vue.d.ts +7 -1
  30. package/dist/components/Select/types.d.ts +2 -0
  31. package/dist/components/Sidebar/SkSidebar.vue.d.ts +1 -1
  32. package/dist/components/Tabs/context.d.ts +6 -0
  33. package/dist/components/Textarea/SkTextarea.vue.d.ts +1 -1
  34. package/dist/components/Tooltip/SkTooltip.vue.d.ts +1 -1
  35. package/dist/composables/injectionKeys.d.ts +9 -0
  36. package/dist/global.d.ts +4 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/sleekspace-ui.css +836 -280
  39. package/dist/sleekspace-ui.es.js +3759 -2545
  40. package/dist/sleekspace-ui.umd.js +3765 -2543
  41. package/dist/static/components/alert.d.ts +2 -1
  42. package/dist/static/components/avatar.d.ts +2 -1
  43. package/dist/static/components/breadcrumbs.d.ts +2 -1
  44. package/dist/static/components/button.d.ts +4 -2
  45. package/dist/static/components/card.d.ts +2 -1
  46. package/dist/static/components/checkbox.d.ts +2 -1
  47. package/dist/static/components/colorPicker.d.ts +2 -1
  48. package/dist/static/components/divider.d.ts +2 -1
  49. package/dist/static/components/dropdown.d.ts +2 -1
  50. package/dist/static/components/field.d.ts +2 -1
  51. package/dist/static/components/group.d.ts +2 -1
  52. package/dist/static/components/input.d.ts +4 -2
  53. package/dist/static/components/inputGroup.d.ts +8 -0
  54. package/dist/static/components/inputGroupAddon.d.ts +7 -0
  55. package/dist/static/components/navBar.d.ts +2 -1
  56. package/dist/static/components/numberInput.d.ts +4 -2
  57. package/dist/static/components/page.d.ts +2 -1
  58. package/dist/static/components/pagination.d.ts +2 -1
  59. package/dist/static/components/panel.d.ts +2 -1
  60. package/dist/static/components/progress.d.ts +2 -1
  61. package/dist/static/components/radio.d.ts +2 -1
  62. package/dist/static/components/select.d.ts +4 -2
  63. package/dist/static/components/sidebar.d.ts +2 -1
  64. package/dist/static/components/skeleton.d.ts +2 -1
  65. package/dist/static/components/slider.d.ts +2 -1
  66. package/dist/static/components/spinner.d.ts +2 -1
  67. package/dist/static/components/switchInput.d.ts +2 -1
  68. package/dist/static/components/table.d.ts +2 -1
  69. package/dist/static/components/tag.d.ts +2 -1
  70. package/dist/static/components/tagsInput.d.ts +2 -1
  71. package/dist/static/components/textarea.d.ts +2 -1
  72. package/dist/static/components/toolbar.d.ts +2 -1
  73. package/dist/static/components/tooltip.d.ts +2 -1
  74. package/dist/static/h.d.ts +2 -0
  75. package/dist/static/index.cjs.js +1 -1
  76. package/dist/static/index.d.ts +6 -0
  77. package/dist/static/index.es.js +366 -216
  78. package/dist/static/render.d.ts +2 -1
  79. package/dist/static/stringH.d.ts +2 -0
  80. package/dist/static/types.d.ts +5 -0
  81. package/dist/tailwind.css +222 -0
  82. package/dist/tokens.css +0 -223
  83. package/dist/types/corners.d.ts +1 -0
  84. package/dist/utils/slots.d.ts +6 -0
  85. package/llms-full.txt +17 -9
  86. package/package.json +9 -3
  87. package/src/components/Accordion/SkAccordion.vue +5 -2
  88. package/src/components/Accordion/SkAccordionItem.vue +7 -4
  89. package/src/components/Accordion/context.ts +23 -0
  90. package/src/components/Alert/SkAlert.vue +4 -2
  91. package/src/components/Autocomplete/SkAutocomplete.test.ts +83 -0
  92. package/src/components/Autocomplete/SkAutocomplete.vue +305 -0
  93. package/src/components/Autocomplete/SkAutocompleteEmpty.vue +39 -0
  94. package/src/components/Autocomplete/SkAutocompleteGroup.vue +46 -0
  95. package/src/components/Autocomplete/SkAutocompleteGroupLabel.vue +39 -0
  96. package/src/components/Autocomplete/SkAutocompleteItem.vue +85 -0
  97. package/src/components/Autocomplete/SkAutocompleteSeparator.vue +39 -0
  98. package/src/components/Autocomplete/index.ts +13 -0
  99. package/src/components/Autocomplete/types.ts +10 -0
  100. package/src/components/Breadcrumbs/SkBreadcrumbItem.vue +8 -3
  101. package/src/components/Breadcrumbs/SkBreadcrumbSeparator.vue +8 -2
  102. package/src/components/Breadcrumbs/SkBreadcrumbs.vue +11 -14
  103. package/src/components/Breadcrumbs/context.ts +20 -0
  104. package/src/components/Button/SkButton.vue +54 -11
  105. package/src/components/Button/types.ts +6 -0
  106. package/src/components/Card/SkCard.vue +12 -5
  107. package/src/components/Checkbox/SkCheckbox.vue +9 -2
  108. package/src/components/ColorPicker/SkColorPicker.vue +27 -5
  109. package/src/components/ContextMenu/SkContextMenu.vue +4 -1
  110. package/src/components/ContextMenu/SkContextMenuSubmenu.vue +5 -2
  111. package/src/components/ContextMenu/context.ts +17 -0
  112. package/src/components/Dropdown/SkDropdown.vue +2 -1
  113. package/src/components/Dropdown/SkDropdownSubmenu.vue +4 -3
  114. package/src/components/Dropdown/context.ts +16 -0
  115. package/src/components/Field/SkField.test.ts +88 -0
  116. package/src/components/Field/SkField.vue +15 -7
  117. package/src/components/Input/SkInput.test.ts +61 -0
  118. package/src/components/Input/SkInput.vue +42 -7
  119. package/src/components/Input/types.ts +2 -0
  120. package/src/components/InputGroup/SkInputGroup.test.ts +171 -0
  121. package/src/components/InputGroup/SkInputGroup.vue +131 -0
  122. package/src/components/InputGroup/SkInputGroupAddon.test.ts +104 -0
  123. package/src/components/InputGroup/SkInputGroupAddon.vue +107 -0
  124. package/src/components/InputGroup/types.ts +27 -0
  125. package/src/components/Listbox/SkListbox.vue +27 -6
  126. package/src/components/Modal/SkModal.vue +11 -4
  127. package/src/components/NavBar/SkNavBar.vue +5 -4
  128. package/src/components/NumberInput/SkNumberInput.vue +49 -8
  129. package/src/components/NumberInput/types.ts +2 -0
  130. package/src/components/Page/SkPage.vue +18 -15
  131. package/src/components/Pagination/SkPagination.vue +6 -3
  132. package/src/components/Pagination/SkPaginationItem.vue +8 -5
  133. package/src/components/Pagination/context.ts +19 -0
  134. package/src/components/Panel/types.ts +3 -2
  135. package/src/components/Popover/SkPopover.vue +11 -4
  136. package/src/components/Radio/SkRadio.vue +14 -4
  137. package/src/components/Radio/SkRadioGroup.vue +4 -2
  138. package/src/components/Radio/context.ts +17 -0
  139. package/src/components/Select/SkSelect.vue +39 -7
  140. package/src/components/Select/types.ts +2 -0
  141. package/src/components/Switch/SkSwitch.vue +14 -13
  142. package/src/components/Tabs/SkTab.vue +10 -3
  143. package/src/components/Tabs/SkTabList.vue +4 -2
  144. package/src/components/Tabs/SkTabs.vue +5 -3
  145. package/src/components/Tabs/context.ts +19 -0
  146. package/src/components/TagsInput/SkTagsInput.vue +28 -7
  147. package/src/components/Textarea/SkTextarea.vue +27 -6
  148. package/src/components/TreeView/SkTreeItem.vue +10 -2
  149. package/src/composables/injectionKeys.ts +52 -0
  150. package/src/index.ts +28 -0
  151. package/src/static/__tests__/parity.test.ts +2 -1
  152. package/src/static/__tests__/parityHarness.ts +5 -2
  153. package/src/static/components/__tests__/helpers.test.ts +191 -99
  154. package/src/static/components/alert.ts +12 -11
  155. package/src/static/components/avatar.ts +15 -16
  156. package/src/static/components/breadcrumbs.ts +3 -2
  157. package/src/static/components/button.ts +23 -27
  158. package/src/static/components/card.ts +3 -2
  159. package/src/static/components/checkbox.ts +11 -14
  160. package/src/static/components/colorPicker.ts +7 -9
  161. package/src/static/components/divider.ts +4 -3
  162. package/src/static/components/dropdown.ts +15 -6
  163. package/src/static/components/field.ts +32 -15
  164. package/src/static/components/group.ts +3 -2
  165. package/src/static/components/input.ts +20 -15
  166. package/src/static/components/inputGroup.ts +30 -0
  167. package/src/static/components/inputGroupAddon.ts +29 -0
  168. package/src/static/components/navBar.ts +30 -17
  169. package/src/static/components/numberInput.ts +17 -17
  170. package/src/static/components/page.ts +3 -2
  171. package/src/static/components/pagination.ts +3 -2
  172. package/src/static/components/panel.ts +3 -2
  173. package/src/static/components/progress.ts +3 -2
  174. package/src/static/components/radio.ts +14 -20
  175. package/src/static/components/select.ts +18 -15
  176. package/src/static/components/sidebar.ts +9 -13
  177. package/src/static/components/skeleton.ts +7 -10
  178. package/src/static/components/slider.ts +7 -9
  179. package/src/static/components/spinner.ts +22 -22
  180. package/src/static/components/switchInput.ts +12 -14
  181. package/src/static/components/table.ts +8 -10
  182. package/src/static/components/tag.ts +17 -11
  183. package/src/static/components/tagsInput.ts +3 -3
  184. package/src/static/components/textarea.ts +8 -13
  185. package/src/static/components/toolbar.ts +7 -10
  186. package/src/static/components/tooltip.ts +3 -2
  187. package/src/static/generated/defaults.ts +25 -9
  188. package/src/static/generated/propTypes.ts +19 -2
  189. package/src/static/h.ts +16 -0
  190. package/src/static/index.ts +8 -0
  191. package/src/static/render.test.ts +14 -10
  192. package/src/static/render.ts +33 -18
  193. package/src/static/specs.test.ts +1 -0
  194. package/src/static/specs.ts +22 -2
  195. package/src/static/stringH.ts +104 -0
  196. package/src/static/types.ts +25 -0
  197. package/src/styles/components/_autocomplete.scss +498 -0
  198. package/src/styles/components/_button.scss +55 -6
  199. package/src/styles/components/_index.scss +2 -0
  200. package/src/styles/components/_input-group.scss +292 -0
  201. package/src/styles/components/_input.scss +57 -9
  202. package/src/styles/components/_number-input.scss +88 -14
  203. package/src/styles/components/_select.scss +56 -9
  204. package/src/styles/mixins/_cut-border.scss +83 -0
  205. package/src/styles/tailwind.scss +262 -0
  206. package/src/styles/tokens.scss +8 -255
  207. package/src/types/corners.ts +10 -0
  208. package/src/utils/slots.test.ts +89 -0
  209. package/src/utils/slots.ts +80 -0
  210. package/web-types.json +392 -12
@@ -8,6 +8,8 @@
8
8
 
9
9
  import { describe, expect, it } from 'vitest';
10
10
 
11
+ import { stringH } from '../../stringH';
12
+
11
13
  import { panel } from '../panel';
12
14
  import { card } from '../card';
13
15
  import { alert } from '../alert';
@@ -39,6 +41,8 @@ import { switchInput } from '../switchInput';
39
41
  import { numberInput } from '../numberInput';
40
42
  import { tagsInput } from '../tagsInput';
41
43
  import { dropdown } from '../dropdown';
44
+ import { inputGroup } from '../inputGroup';
45
+ import { inputGroupAddon } from '../inputGroupAddon';
42
46
 
43
47
  //----------------------------------------------------------------------------------------------------------------------
44
48
  // Task 8: Panel pilot
@@ -48,27 +52,27 @@ describe('panel()', () =>
48
52
  {
49
53
  it('default', () =>
50
54
  {
51
- expect(panel({}, 'x')).toBe('<div class="sk-panel">x</div>');
55
+ expect(panel(stringH, {}, 'x')).toBe('<div class="sk-panel">x</div>');
52
56
  });
53
57
 
54
58
  it('kind', () =>
55
59
  {
56
- expect(panel({ kind: 'primary' }, 'x')).toBe('<div class="sk-panel sk-primary">x</div>');
60
+ expect(panel(stringH, { kind: 'primary' }, 'x')).toBe('<div class="sk-panel sk-primary">x</div>');
57
61
  });
58
62
 
59
63
  it('size', () =>
60
64
  {
61
- expect(panel({ size: 'lg' }, 'x')).toBe('<div class="sk-panel sk-lg sk-size-lg">x</div>');
65
+ expect(panel(stringH, { size: 'lg' }, 'x')).toBe('<div class="sk-panel sk-lg sk-size-lg">x</div>');
62
66
  });
63
67
 
64
68
  it('noBorder flag', () =>
65
69
  {
66
- expect(panel({ noBorder: true }, 'x')).toBe('<div class="sk-panel sk-no-border">x</div>');
70
+ expect(panel(stringH, { noBorder: true }, 'x')).toBe('<div class="sk-panel sk-no-border">x</div>');
67
71
  });
68
72
 
69
73
  it('custom colors emit inline style', () =>
70
74
  {
71
- expect(panel({ baseColor: '#f00', textColor: '#fff' }, 'x'))
75
+ expect(panel(stringH, { baseColor: '#f00', textColor: '#fff' }, 'x'))
72
76
  .toBe('<div class="sk-panel" style="--sk-panel-color-base: #f00; --sk-panel-fg: #fff;">x</div>');
73
77
  });
74
78
  });
@@ -81,12 +85,12 @@ describe('card()', () =>
81
85
  {
82
86
  it('default', () =>
83
87
  {
84
- expect(card({}, 'x')).toBe('<div class="sk-card">x</div>');
88
+ expect(card(stringH, {}, 'x')).toBe('<div class="sk-card">x</div>');
85
89
  });
86
90
 
87
91
  it('kind', () =>
88
92
  {
89
- expect(card({ kind: 'primary' }, 'x')).toBe('<div class="sk-card sk-primary">x</div>');
93
+ expect(card(stringH, { kind: 'primary' }, 'x')).toBe('<div class="sk-card sk-primary">x</div>');
90
94
  });
91
95
  });
92
96
 
@@ -97,7 +101,7 @@ describe('alert()', () =>
97
101
  // Default kind is 'info' — feedback kind, so icon is shown.
98
102
  it('default (info kind — has icon)', () =>
99
103
  {
100
- const html = alert({}, 'x');
104
+ const html = alert(stringH, {}, 'x');
101
105
  expect(html).toContain('class="sk-alert sk-info"');
102
106
  expect(html).toContain('role="alert"');
103
107
  expect(html).toContain('<div class="sk-alert-icon">');
@@ -106,7 +110,7 @@ describe('alert()', () =>
106
110
 
107
111
  it('warning kind (has icon)', () =>
108
112
  {
109
- const html = alert({ kind: 'warning' }, 'x');
113
+ const html = alert(stringH, { kind: 'warning' }, 'x');
110
114
  expect(html).toContain('class="sk-alert sk-warning"');
111
115
  expect(html).toContain('<div class="sk-alert-icon">');
112
116
  expect(html).toContain('<div class="sk-alert-content">x</div>');
@@ -114,7 +118,7 @@ describe('alert()', () =>
114
118
 
115
119
  it('neutral kind (no icon)', () =>
116
120
  {
117
- const html = alert({ kind: 'neutral' }, 'x');
121
+ const html = alert(stringH, { kind: 'neutral' }, 'x');
118
122
  expect(html).toContain('class="sk-alert sk-neutral"');
119
123
  expect(html).not.toContain('sk-alert-icon');
120
124
  expect(html).toContain('<div class="sk-alert-content">x</div>');
@@ -122,14 +126,14 @@ describe('alert()', () =>
122
126
 
123
127
  it('icon: false suppresses icon on feedback kind', () =>
124
128
  {
125
- const html = alert({ kind: 'danger', icon: false }, 'x');
129
+ const html = alert(stringH, { kind: 'danger', icon: false }, 'x');
126
130
  expect(html).not.toContain('sk-alert-icon');
127
131
  expect(html).toContain('<div class="sk-alert-content">x</div>');
128
132
  });
129
133
 
130
134
  it('custom icon string', () =>
131
135
  {
132
- const html = alert({ kind: 'neutral', icon: '<span>!</span>' }, 'x');
136
+ const html = alert(stringH, { kind: 'neutral', icon: '<span>!</span>' }, 'x');
133
137
  expect(html).toContain('<div class="sk-alert-icon"><span>!</span></div>');
134
138
  expect(html).toContain('<div class="sk-alert-content">x</div>');
135
139
  });
@@ -142,25 +146,25 @@ describe('divider()', () =>
142
146
  // Default: horizontal orientation, neutral kind, md size — matches Vue's withDefaults().
143
147
  it('default', () =>
144
148
  {
145
- expect(divider({})).toBe('<hr class="sk-divider sk-horizontal sk-neutral sk-md" role="separator">');
149
+ expect(divider(stringH, {})).toBe('<hr class="sk-divider sk-horizontal sk-neutral sk-md" role="separator" />');
146
150
  });
147
151
 
148
152
  it('orientation vertical', () =>
149
153
  {
150
- expect(divider({ orientation: 'vertical' }))
151
- .toBe('<hr class="sk-divider sk-vertical sk-neutral sk-md" role="separator">');
154
+ expect(divider(stringH, { orientation: 'vertical' }))
155
+ .toBe('<hr class="sk-divider sk-vertical sk-neutral sk-md" role="separator" />');
152
156
  });
153
157
 
154
158
  it('kind', () =>
155
159
  {
156
- expect(divider({ kind: 'primary' }))
157
- .toBe('<hr class="sk-divider sk-horizontal sk-primary sk-md" role="separator">');
160
+ expect(divider(stringH, { kind: 'primary' }))
161
+ .toBe('<hr class="sk-divider sk-horizontal sk-primary sk-md" role="separator" />');
158
162
  });
159
163
 
160
164
  it('subtle variant', () =>
161
165
  {
162
- expect(divider({ variant: 'subtle' }))
163
- .toBe('<hr class="sk-divider sk-horizontal sk-neutral sk-md sk-subtle" role="separator">');
166
+ expect(divider(stringH, { variant: 'subtle' }))
167
+ .toBe('<hr class="sk-divider sk-horizontal sk-neutral sk-md sk-subtle" role="separator" />');
164
168
  });
165
169
  });
166
170
 
@@ -170,12 +174,12 @@ describe('page()', () =>
170
174
  {
171
175
  it('default', () =>
172
176
  {
173
- expect(page({}, 'x')).toBe('<div class="sk-page">x</div>');
177
+ expect(page(stringH, {}, 'x')).toBe('<div class="sk-page">x</div>');
174
178
  });
175
179
 
176
180
  it('flush flag', () =>
177
181
  {
178
- expect(page({ flush: true }, 'x')).toBe('<div class="sk-page sk-flush">x</div>');
182
+ expect(page(stringH, { flush: true }, 'x')).toBe('<div class="sk-page sk-flush">x</div>');
179
183
  });
180
184
  });
181
185
 
@@ -185,12 +189,13 @@ describe('group()', () =>
185
189
  {
186
190
  it('default', () =>
187
191
  {
188
- expect(group({}, 'x')).toBe('<div class="sk-group">x</div>');
192
+ expect(group(stringH, {}, 'x')).toBe('<div class="sk-group">x</div>');
189
193
  });
190
194
 
191
195
  it('orientation', () =>
192
196
  {
193
- expect(group({ orientation: 'vertical' }, 'x')).toBe('<div class="sk-group sk-orientation-vertical">x</div>');
197
+ expect(group(stringH, { orientation: 'vertical' }, 'x'))
198
+ .toBe('<div class="sk-group sk-orientation-vertical">x</div>');
194
199
  });
195
200
  });
196
201
 
@@ -201,27 +206,28 @@ describe('skeleton()', () =>
201
206
  // Default: text variant + shimmer animation — matches Vue's withDefaults().
202
207
  it('default', () =>
203
208
  {
204
- expect(skeleton({})).toBe('<div class="sk-skeleton sk-text sk-shimmer"></div>');
209
+ expect(skeleton(stringH, {})).toBe('<div class="sk-skeleton sk-text sk-shimmer"></div>');
205
210
  });
206
211
 
207
212
  it('variant circular', () =>
208
213
  {
209
- expect(skeleton({ variant: 'circular' })).toBe('<div class="sk-skeleton sk-circular sk-shimmer"></div>');
214
+ expect(skeleton(stringH, { variant: 'circular' }))
215
+ .toBe('<div class="sk-skeleton sk-circular sk-shimmer"></div>');
210
216
  });
211
217
 
212
218
  it('animation: none suppresses animation class', () =>
213
219
  {
214
- expect(skeleton({ animation: 'none' })).toBe('<div class="sk-skeleton sk-text"></div>');
220
+ expect(skeleton(stringH, { animation: 'none' })).toBe('<div class="sk-skeleton sk-text"></div>');
215
221
  });
216
222
 
217
223
  it('pulse animation', () =>
218
224
  {
219
- expect(skeleton({ animation: 'pulse' })).toBe('<div class="sk-skeleton sk-text sk-pulse"></div>');
225
+ expect(skeleton(stringH, { animation: 'pulse' })).toBe('<div class="sk-skeleton sk-text sk-pulse"></div>');
220
226
  });
221
227
 
222
228
  it('width prop emits inline style', () =>
223
229
  {
224
- expect(skeleton({ width: '200px' })).toBe(
230
+ expect(skeleton(stringH, { width: '200px' })).toBe(
225
231
  '<div class="sk-skeleton sk-text sk-shimmer" style="width: 200px;"></div>'
226
232
  );
227
233
  });
@@ -233,12 +239,12 @@ describe('progress()', () =>
233
239
  {
234
240
  it('default', () =>
235
241
  {
236
- expect(progress({}, '')).toBe('<progress class="sk-progress" />');
242
+ expect(progress(stringH, {}, undefined)).toBe('<progress class="sk-progress" />');
237
243
  });
238
244
 
239
245
  it('kind', () =>
240
246
  {
241
- expect(progress({ kind: 'primary' }, '')).toBe('<progress class="sk-progress sk-primary" />');
247
+ expect(progress(stringH, { kind: 'primary' }, undefined)).toBe('<progress class="sk-progress sk-primary" />');
242
248
  });
243
249
  });
244
250
 
@@ -249,7 +255,7 @@ describe('spinner()', () =>
249
255
  // Default: primary kind, md size, circular variant + inner circular DOM structure.
250
256
  it('default (circular variant, primary kind)', () =>
251
257
  {
252
- const html = spinner({});
258
+ const html = spinner(stringH, {});
253
259
  expect(html).toContain('class="sk-spinner sk-primary sk-md sk-variant-circular"');
254
260
  expect(html).toContain('role="status"');
255
261
  expect(html).toContain('aria-live="polite"');
@@ -261,7 +267,7 @@ describe('spinner()', () =>
261
267
 
262
268
  it('dots variant', () =>
263
269
  {
264
- const html = spinner({ variant: 'dots' });
270
+ const html = spinner(stringH, { variant: 'dots' });
265
271
  expect(html).toContain('sk-variant-dots');
266
272
  expect(html).toContain('<div class="sk-spinner-dots">');
267
273
  expect(html).toContain('<div class="sk-dot"></div>');
@@ -269,14 +275,14 @@ describe('spinner()', () =>
269
275
 
270
276
  it('crosshair variant', () =>
271
277
  {
272
- const html = spinner({ variant: 'crosshair' });
278
+ const html = spinner(stringH, { variant: 'crosshair' });
273
279
  expect(html).toContain('sk-variant-crosshair');
274
280
  expect(html).toContain('<div class="sk-crosshair-loader"></div>');
275
281
  });
276
282
 
277
283
  it('kind and size', () =>
278
284
  {
279
- const html = spinner({ kind: 'accent', size: 'lg' });
285
+ const html = spinner(stringH, { kind: 'accent', size: 'lg' });
280
286
  expect(html).toContain('sk-spinner sk-accent sk-lg sk-variant-circular');
281
287
  });
282
288
  });
@@ -288,7 +294,7 @@ describe('navBar()', () =>
288
294
  // sticky defaults to true — sk-sticky always present unless explicitly disabled.
289
295
  it('default (neutral kind, sticky)', () =>
290
296
  {
291
- const html = navBar({}, 'x');
297
+ const html = navBar(stringH, {}, 'x');
292
298
  expect(html).toContain('class="sk-navbar sk-sticky"');
293
299
  expect(html).toContain('<div class="sk-navbar-content">');
294
300
  expect(html).toContain('<div class="sk-navbar-nav">x</div>');
@@ -296,20 +302,20 @@ describe('navBar()', () =>
296
302
 
297
303
  it('kind', () =>
298
304
  {
299
- const html = navBar({ kind: 'primary' }, 'x');
305
+ const html = navBar(stringH, { kind: 'primary' }, 'x');
300
306
  expect(html).toContain('class="sk-navbar sk-primary sk-sticky"');
301
307
  expect(html).toContain('<div class="sk-navbar-nav">x</div>');
302
308
  });
303
309
 
304
310
  it('sticky: false omits sk-sticky', () =>
305
311
  {
306
- const html = navBar({ sticky: false }, 'x');
312
+ const html = navBar(stringH, { sticky: false }, 'x');
307
313
  expect(html).not.toContain('sk-sticky');
308
314
  });
309
315
 
310
316
  it('brand prop emits sk-navbar-brand', () =>
311
317
  {
312
- const html = navBar({ brand: '<img src="logo.svg" />' }, 'Links');
318
+ const html = navBar(stringH, { brand: '<img src="logo.svg" />' }, 'Links');
313
319
  expect(html).toContain('<div class="sk-navbar-brand"><img src="logo.svg" /></div>');
314
320
  expect(html).toContain('<div class="sk-navbar-nav">Links</div>');
315
321
  });
@@ -322,7 +328,7 @@ describe('toolbar()', () =>
322
328
  // Default corners = all four; default orientation = horizontal.
323
329
  it('default (all corners, horizontal)', () =>
324
330
  {
325
- const html = toolbar({}, 'x');
331
+ const html = toolbar(stringH, {}, 'x');
326
332
  expect(html).toContain(
327
333
  'class="sk-toolbar sk-horizontal sk-cut-top-left sk-cut-top-right sk-cut-bottom-right sk-cut-bottom-left"'
328
334
  );
@@ -332,20 +338,20 @@ describe('toolbar()', () =>
332
338
 
333
339
  it('kind', () =>
334
340
  {
335
- const html = toolbar({ kind: 'primary' }, 'x');
341
+ const html = toolbar(stringH, { kind: 'primary' }, 'x');
336
342
  expect(html).toContain('sk-toolbar sk-primary sk-horizontal');
337
343
  });
338
344
 
339
345
  it('vertical orientation', () =>
340
346
  {
341
- const html = toolbar({ orientation: 'vertical' }, 'x');
347
+ const html = toolbar(stringH, { orientation: 'vertical' }, 'x');
342
348
  expect(html).toContain('sk-vertical');
343
349
  expect(html).not.toContain('sk-horizontal');
344
350
  });
345
351
 
346
352
  it('empty corners', () =>
347
353
  {
348
- const html = toolbar({ corners: [] }, 'x');
354
+ const html = toolbar(stringH, { corners: [] }, 'x');
349
355
  expect(html).not.toContain('sk-cut-');
350
356
  });
351
357
  });
@@ -356,7 +362,7 @@ describe('sidebar()', () =>
356
362
  {
357
363
  it('default (neutral, left side)', () =>
358
364
  {
359
- const html = sidebar({}, 'x');
365
+ const html = sidebar(stringH, {}, 'x');
360
366
  expect(html).toContain('<aside class="sk-sidebar sk-neutral">');
361
367
  expect(html).toContain(
362
368
  'class="sk-panel sk-neutral sk-md sk-cut-bottom-right sk-decoration-bottom-right sk-sidebar-panel"'
@@ -367,14 +373,14 @@ describe('sidebar()', () =>
367
373
 
368
374
  it('kind', () =>
369
375
  {
370
- const html = sidebar({ kind: 'primary' }, 'x');
376
+ const html = sidebar(stringH, { kind: 'primary' }, 'x');
371
377
  expect(html).toContain('<aside class="sk-sidebar sk-primary">');
372
378
  expect(html).toContain('sk-panel sk-primary');
373
379
  });
374
380
 
375
381
  it('right side uses bottom-left corner', () =>
376
382
  {
377
- const html = sidebar({ side: 'right' }, 'x');
383
+ const html = sidebar(stringH, { side: 'right' }, 'x');
378
384
  expect(html).toContain('sk-sidebar-right');
379
385
  expect(html).toContain('sk-cut-bottom-left');
380
386
  expect(html).toContain('sk-decoration-bottom-left');
@@ -387,12 +393,13 @@ describe('breadcrumbs()', () =>
387
393
  {
388
394
  it('default', () =>
389
395
  {
390
- expect(breadcrumbs({}, 'x')).toBe('<nav class="sk-breadcrumbs" aria-label="Breadcrumbs">x</nav>');
396
+ expect(breadcrumbs(stringH, {}, 'x'))
397
+ .toBe('<nav class="sk-breadcrumbs" aria-label="Breadcrumbs">x</nav>');
391
398
  });
392
399
 
393
400
  it('kind', () =>
394
401
  {
395
- expect(breadcrumbs({ kind: 'primary' }, 'x')).toBe(
402
+ expect(breadcrumbs(stringH, { kind: 'primary' }, 'x')).toBe(
396
403
  '<nav class="sk-breadcrumbs sk-primary" aria-label="Breadcrumbs">x</nav>'
397
404
  );
398
405
  });
@@ -404,12 +411,12 @@ describe('pagination()', () =>
404
411
  {
405
412
  it('default', () =>
406
413
  {
407
- expect(pagination({}, 'x')).toBe('<nav class="sk-pagination" aria-label="Pagination">x</nav>');
414
+ expect(pagination(stringH, {}, 'x')).toBe('<nav class="sk-pagination" aria-label="Pagination">x</nav>');
408
415
  });
409
416
 
410
417
  it('kind', () =>
411
418
  {
412
- expect(pagination({ kind: 'primary' }, 'x')).toBe(
419
+ expect(pagination(stringH, { kind: 'primary' }, 'x')).toBe(
413
420
  '<nav class="sk-pagination sk-primary" aria-label="Pagination">x</nav>'
414
421
  );
415
422
  });
@@ -423,21 +430,21 @@ describe('tag()', () =>
423
430
  // Content is always wrapped in sk-tag-content span.
424
431
  it('default (neutral, solid, md — sk-tag-content wrapper)', () =>
425
432
  {
426
- const html = tag({}, 'x');
433
+ const html = tag(stringH, {}, 'x');
427
434
  expect(html).toContain('class="sk-tag sk-neutral sk-solid sk-md"');
428
435
  expect(html).toContain('<span class="sk-tag-content">x</span>');
429
436
  });
430
437
 
431
438
  it('kind', () =>
432
439
  {
433
- const html = tag({ kind: 'primary' }, 'x');
440
+ const html = tag(stringH, { kind: 'primary' }, 'x');
434
441
  expect(html).toContain('class="sk-tag sk-primary sk-solid sk-md"');
435
442
  expect(html).toContain('<span class="sk-tag-content">x</span>');
436
443
  });
437
444
 
438
445
  it('removable emits remove button', () =>
439
446
  {
440
- const html = tag({ removable: true }, 'Label');
447
+ const html = tag(stringH, { removable: true }, 'Label');
441
448
  expect(html).toContain('sk-removable');
442
449
  expect(html).toContain('<span class="sk-tag-content">Label</span>');
443
450
  expect(html).toContain('<button type="button" class="sk-tag-remove" aria-label="Remove">');
@@ -446,7 +453,7 @@ describe('tag()', () =>
446
453
 
447
454
  it('variant and size modifiers', () =>
448
455
  {
449
- const html = tag({ variant: 'outline', size: 'sm' }, 'x');
456
+ const html = tag(stringH, { variant: 'outline', size: 'sm' }, 'x');
450
457
  expect(html).toContain('sk-tag sk-neutral sk-outline sk-sm');
451
458
  });
452
459
  });
@@ -458,7 +465,7 @@ describe('avatar()', () =>
458
465
  // Default: neutral kind, md size, no src/initials → default SVG icon.
459
466
  it('default (neutral, md — SVG icon fallback)', () =>
460
467
  {
461
- const html = avatar({});
468
+ const html = avatar(stringH, {});
462
469
  expect(html).toContain('class="sk-avatar sk-neutral sk-md"');
463
470
  expect(html).toContain('<svg class="sk-avatar-icon"');
464
471
  expect(html).toContain('fill="currentColor"');
@@ -466,29 +473,29 @@ describe('avatar()', () =>
466
473
 
467
474
  it('kind emits kind class', () =>
468
475
  {
469
- const html = avatar({ kind: 'primary' });
476
+ const html = avatar(stringH, { kind: 'primary' });
470
477
  expect(html).toContain('class="sk-avatar sk-primary sk-md"');
471
478
  expect(html).toContain('<svg class="sk-avatar-icon"');
472
479
  });
473
480
 
474
481
  it('src emits <img> element', () =>
475
482
  {
476
- const html = avatar({ src: '/x.jpg', alt: 'Jane Doe' });
477
- expect(html).toContain('<img src="/x.jpg" alt="Jane Doe" class="sk-avatar-image">');
483
+ const html = avatar(stringH, { src: '/x.jpg', alt: 'Jane Doe' });
484
+ expect(html).toContain('<img src="/x.jpg" alt="Jane Doe" class="sk-avatar-image" />');
478
485
  expect(html).not.toContain('sk-avatar-icon');
479
486
  expect(html).not.toContain('sk-avatar-initials');
480
487
  });
481
488
 
482
489
  it('initials emits <span class="sk-avatar-initials">', () =>
483
490
  {
484
- const html = avatar({ initials: 'jd' });
491
+ const html = avatar(stringH, { initials: 'jd' });
485
492
  expect(html).toContain('<span class="sk-avatar-initials">JD</span>');
486
493
  expect(html).not.toContain('sk-avatar-icon');
487
494
  });
488
495
 
489
496
  it('initials truncated to 2 chars, uppercased', () =>
490
497
  {
491
- const html = avatar({ initials: 'john' });
498
+ const html = avatar(stringH, { initials: 'john' });
492
499
  expect(html).toContain('<span class="sk-avatar-initials">JO</span>');
493
500
  });
494
501
  });
@@ -500,7 +507,7 @@ describe('field()', () =>
500
507
  // Default: labelPosition='top' → sk-label-top, children wrapped in sk-field-input-wrapper.
501
508
  it('default (sk-label-top, input wrapper)', () =>
502
509
  {
503
- const html = field({}, 'x');
510
+ const html = field(stringH, {}, 'x');
504
511
  expect(html).toContain('class="sk-field sk-label-top"');
505
512
  expect(html).toContain('<div class="sk-field-input-wrapper">x</div>');
506
513
  expect(html).not.toContain('sk-field-label');
@@ -509,20 +516,20 @@ describe('field()', () =>
509
516
 
510
517
  it('label prop emits <label class="sk-field-label">', () =>
511
518
  {
512
- const html = field({ label: 'Email' }, 'x');
519
+ const html = field(stringH, { label: 'Email' }, 'x');
513
520
  expect(html).toContain('<label class="sk-field-label">Email</label>');
514
521
  expect(html).toContain('<div class="sk-field-input-wrapper">x</div>');
515
522
  });
516
523
 
517
524
  it('required adds asterisk span inside label', () =>
518
525
  {
519
- const html = field({ label: 'Name', required: true }, 'x');
526
+ const html = field(stringH, { label: 'Name', required: true }, 'x');
520
527
  expect(html).toContain('Name<span class="sk-field-required">*</span>');
521
528
  });
522
529
 
523
530
  it('error prop adds sk-has-error class and sk-field-error paragraph', () =>
524
531
  {
525
- const html = field({ error: 'Required' }, 'x');
532
+ const html = field(stringH, { error: 'Required' }, 'x');
526
533
  expect(html).toContain('class="sk-field sk-label-top sk-has-error"');
527
534
  expect(html).toContain('<p class="sk-field-error">Required</p>');
528
535
  expect(html).not.toContain('sk-field-description');
@@ -530,20 +537,20 @@ describe('field()', () =>
530
537
 
531
538
  it('description shown when no error', () =>
532
539
  {
533
- const html = field({ description: 'Help text' }, 'x');
540
+ const html = field(stringH, { description: 'Help text' }, 'x');
534
541
  expect(html).toContain('<p class="sk-field-description">Help text</p>');
535
542
  });
536
543
 
537
544
  it('description hidden when error is present', () =>
538
545
  {
539
- const html = field({ description: 'Help', error: 'Bad' }, 'x');
546
+ const html = field(stringH, { description: 'Help', error: 'Bad' }, 'x');
540
547
  expect(html).toContain('<p class="sk-field-error">Bad</p>');
541
548
  expect(html).not.toContain('sk-field-description');
542
549
  });
543
550
 
544
551
  it('labelPosition left', () =>
545
552
  {
546
- const html = field({ labelPosition: 'left' }, 'x');
553
+ const html = field(stringH, { labelPosition: 'left' }, 'x');
547
554
  expect(html).toContain('class="sk-field sk-label-left"');
548
555
  });
549
556
  });
@@ -556,7 +563,7 @@ describe('table()', () =>
556
563
  // hoverable=true, bordered=true, innerBorders=false → sk-no-inner-borders.
557
564
  it('default (wrapper div + table with hoverable/bordered/no-inner-borders)', () =>
558
565
  {
559
- const html = table({}, 'x');
566
+ const html = table(stringH, {}, 'x');
560
567
  expect(html).toContain('class="sk-table-wrapper sk-table-wrapper-neutral"');
561
568
  expect(html).toContain('class="sk-table sk-neutral sk-default sk-hoverable sk-bordered sk-no-inner-borders"');
562
569
  expect(html).toContain('>x</table>');
@@ -564,32 +571,32 @@ describe('table()', () =>
564
571
 
565
572
  it('kind propagates to both wrapper and table', () =>
566
573
  {
567
- const html = table({ kind: 'primary' }, 'x');
574
+ const html = table(stringH, { kind: 'primary' }, 'x');
568
575
  expect(html).toContain('sk-table-wrapper-primary');
569
576
  expect(html).toContain('class="sk-table sk-primary sk-default sk-hoverable sk-bordered sk-no-inner-borders"');
570
577
  });
571
578
 
572
579
  it('striped adds sk-striped', () =>
573
580
  {
574
- const html = table({ striped: true }, 'x');
581
+ const html = table(stringH, { striped: true }, 'x');
575
582
  expect(html).toContain('sk-striped');
576
583
  });
577
584
 
578
585
  it('hoverable: false removes sk-hoverable', () =>
579
586
  {
580
- const html = table({ hoverable: false }, 'x');
587
+ const html = table(stringH, { hoverable: false }, 'x');
581
588
  expect(html).not.toContain('sk-hoverable');
582
589
  });
583
590
 
584
591
  it('darkBackground adds sk-dark-background to wrapper', () =>
585
592
  {
586
- const html = table({ darkBackground: true }, 'x');
593
+ const html = table(stringH, { darkBackground: true }, 'x');
587
594
  expect(html).toContain('sk-dark-background');
588
595
  });
589
596
 
590
597
  it('subtle adds sk-subtle to both wrapper and table', () =>
591
598
  {
592
- const html = table({ subtle: true }, 'x');
599
+ const html = table(stringH, { subtle: true }, 'x');
593
600
  expect(html).toContain('sk-table-wrapper sk-table-wrapper-neutral sk-subtle');
594
601
  expect(html).toContain('sk-subtle');
595
602
  });
@@ -601,12 +608,13 @@ describe('tooltip()', () =>
601
608
  {
602
609
  it('default', () =>
603
610
  {
604
- expect(tooltip({}, 'x')).toBe('<div class="sk-tooltip" role="tooltip">x</div>');
611
+ expect(tooltip(stringH, {}, 'x')).toBe('<div class="sk-tooltip" role="tooltip">x</div>');
605
612
  });
606
613
 
607
614
  it('kind', () =>
608
615
  {
609
- expect(tooltip({ kind: 'primary' }, 'x')).toBe('<div class="sk-tooltip sk-primary" role="tooltip">x</div>');
616
+ expect(tooltip(stringH, { kind: 'primary' }, 'x'))
617
+ .toBe('<div class="sk-tooltip sk-primary" role="tooltip">x</div>');
610
618
  });
611
619
  });
612
620
 
@@ -618,29 +626,36 @@ describe('button()', () =>
618
626
  {
619
627
  it('default emits <button type="button"> with chrome span', () =>
620
628
  {
621
- expect(button({}, 'Save'))
629
+ expect(button(stringH, {}, 'Save'))
622
630
  .toBe('<button class="sk-button" type="button"><span class="sk-button-chrome">Save</span></button>');
623
631
  });
624
632
 
625
633
  it('kind + size + variant', () =>
626
634
  {
627
- expect(button({ kind: 'primary', size: 'lg', variant: 'outline' }, 'Click'))
635
+ expect(button(stringH, { kind: 'primary', size: 'lg', variant: 'outline' }, 'Click'))
628
636
  .toBe('<button class="sk-button sk-primary sk-lg sk-size-lg sk-outline" type="button">'
629
637
  + '<span class="sk-button-chrome">Click</span></button>');
630
638
  });
631
639
 
632
640
  it('loading and pressed emit aria attrs and boolean classes', () =>
633
641
  {
634
- expect(button({ loading: true, pressed: true }, 'x'))
642
+ expect(button(stringH, { loading: true, pressed: true }, 'x'))
635
643
  .toBe('<button class="sk-button sk-loading sk-pressed" type="button" aria-busy="true" aria-pressed="true">'
636
644
  + '<span class="sk-button-chrome">x</span></button>');
637
645
  });
638
646
 
639
647
  it('href renders as <a>, not <button>', () =>
640
648
  {
641
- expect(button({ href: '/docs' }, 'Docs'))
649
+ expect(button(stringH, { href: '/docs' }, 'Docs'))
642
650
  .toBe('<a class="sk-button" href="/docs"><span class="sk-button-chrome">Docs</span></a>');
643
651
  });
652
+
653
+ it('emits corner classes from corners array', () =>
654
+ {
655
+ const html = button(stringH, { corners: [ 'top-right' ] });
656
+ expect(html).toContain('sk-cut-top-right');
657
+ expect(html).not.toContain('sk-cut-top-left');
658
+ });
644
659
  });
645
660
 
646
661
  //----------------------------------------------------------------------------------------------------------------------
@@ -651,15 +666,30 @@ describe('input()', () =>
651
666
  {
652
667
  it('default emits void input', () =>
653
668
  {
654
- expect(input({}))
669
+ expect(input(stringH, {}))
655
670
  .toBe('<input class="sk-input" />');
656
671
  });
657
672
 
658
673
  it('passes through value, placeholder, name via props', () =>
659
674
  {
660
- expect(input({ type: 'text', value: 'hi', placeholder: 'Your name', name: 'user' }))
675
+ expect(input(stringH, { type: 'text', value: 'hi', placeholder: 'Your name', name: 'user' }))
661
676
  .toBe('<input class="sk-input" type="text" value="hi" placeholder="Your name" name="user" />');
662
677
  });
678
+
679
+ it('emits corner classes from corners array', () =>
680
+ {
681
+ const html = input(stringH, { corners: [ 'top-left', 'bottom-right' ] });
682
+ expect(html).toContain('sk-cut-top-left');
683
+ expect(html).toContain('sk-cut-bottom-right');
684
+ expect(html).not.toContain('sk-cut-top-right');
685
+ expect(html).not.toContain('sk-cut-bottom-left');
686
+ });
687
+
688
+ it('omits corner classes when corners is unset', () =>
689
+ {
690
+ const html = input(stringH, {});
691
+ expect(html).not.toContain('sk-cut-');
692
+ });
663
693
  });
664
694
 
665
695
  //----------------------------------------------------------------------------------------------------------------------
@@ -668,13 +698,13 @@ describe('textarea()', () =>
668
698
  {
669
699
  it('default', () =>
670
700
  {
671
- expect(textarea({}, 'content'))
701
+ expect(textarea(stringH, {}, 'content'))
672
702
  .toBe('<textarea class="sk-textarea">content</textarea>');
673
703
  });
674
704
 
675
705
  it('passes through placeholder and name', () =>
676
706
  {
677
- expect(textarea({ placeholder: 'Enter text', name: 'bio' }, ''))
707
+ expect(textarea(stringH, { placeholder: 'Enter text', name: 'bio' }, ''))
678
708
  .toBe('<textarea class="sk-textarea" placeholder="Enter text" name="bio"></textarea>');
679
709
  });
680
710
  });
@@ -686,9 +716,16 @@ describe('select()', () =>
686
716
  it('renders with option children', () =>
687
717
  {
688
718
  const options = '<option value="a">A</option><option value="b">B</option>';
689
- expect(select({}, options))
719
+ expect(select(stringH, {}, options))
690
720
  .toBe('<select class="sk-select"><option value="a">A</option><option value="b">B</option></select>');
691
721
  });
722
+
723
+ it('emits corner classes from corners array', () =>
724
+ {
725
+ const html = select(stringH, { corners: [ 'top-left' ] });
726
+ expect(html).toContain('sk-cut-top-left');
727
+ expect(html).not.toContain('sk-cut-top-right');
728
+ });
692
729
  });
693
730
 
694
731
  //----------------------------------------------------------------------------------------------------------------------
@@ -697,13 +734,13 @@ describe('slider()', () =>
697
734
  {
698
735
  it('default emits type=range', () =>
699
736
  {
700
- expect(slider({}))
737
+ expect(slider(stringH, {}))
701
738
  .toBe('<input class="sk-slider" type="range" />');
702
739
  });
703
740
 
704
741
  it('passes through min, max, step, value', () =>
705
742
  {
706
- expect(slider({ min: '0', max: '100', step: '5', value: '50' }))
743
+ expect(slider(stringH, { min: '0', max: '100', step: '5', value: '50' }))
707
744
  .toBe('<input class="sk-slider" type="range" min="0" max="100" step="5" value="50" />');
708
745
  });
709
746
  });
@@ -714,13 +751,13 @@ describe('colorPicker()', () =>
714
751
  {
715
752
  it('default emits type=color', () =>
716
753
  {
717
- expect(colorPicker({}))
754
+ expect(colorPicker(stringH, {}))
718
755
  .toBe('<input class="sk-color-picker" type="color" />');
719
756
  });
720
757
 
721
758
  it('passes through value', () =>
722
759
  {
723
- expect(colorPicker({ value: '#ff0000' }))
760
+ expect(colorPicker(stringH, { value: '#ff0000' }))
724
761
  .toBe('<input class="sk-color-picker" type="color" value="#ff0000" />');
725
762
  });
726
763
  });
@@ -733,14 +770,15 @@ describe('checkbox()', () =>
733
770
  {
734
771
  it('default', () =>
735
772
  {
736
- expect(checkbox({}, 'Agree'))
737
- .toBe('<label class="sk-checkbox"><input type="checkbox" /><span class="sk-checkbox-box"></span>'
773
+ expect(checkbox(stringH, {}, 'Agree'))
774
+ .toBe('<label class="sk-checkbox"><input type="checkbox" />'
775
+ + '<span class="sk-checkbox-box"></span>'
738
776
  + '<span class="sk-checkbox-label">Agree</span></label>');
739
777
  });
740
778
 
741
779
  it('checked + disabled', () =>
742
780
  {
743
- expect(checkbox({ checked: true, disabled: true }, 'x'))
781
+ expect(checkbox(stringH, { checked: true, disabled: true }, 'x'))
744
782
  .toBe('<label class="sk-checkbox"><input type="checkbox" checked disabled />'
745
783
  + '<span class="sk-checkbox-box"></span><span class="sk-checkbox-label">x</span></label>');
746
784
  });
@@ -752,7 +790,7 @@ describe('radio()', () =>
752
790
  {
753
791
  it('default with name and value', () =>
754
792
  {
755
- expect(radio({ name: 'flavor', value: 'chocolate' }, 'Chocolate'))
793
+ expect(radio(stringH, { name: 'flavor', value: 'chocolate' }, 'Chocolate'))
756
794
  .toBe('<label class="sk-radio"><input type="radio" name="flavor" value="chocolate" />'
757
795
  + '<span class="sk-radio-dot"></span><span class="sk-radio-label">Chocolate</span></label>');
758
796
  });
@@ -764,7 +802,7 @@ describe('switchInput()', () =>
764
802
  {
765
803
  it('default', () =>
766
804
  {
767
- expect(switchInput({ name: 'notify' }, 'Notifications'))
805
+ expect(switchInput(stringH, { name: 'notify' }, 'Notifications'))
768
806
  .toBe('<label class="sk-switch"><input type="checkbox" name="notify" />'
769
807
  + '<span class="sk-switch-track"><span class="sk-switch-thumb"></span></span>'
770
808
  + '<span class="sk-switch-label">Notifications</span></label>');
@@ -777,7 +815,7 @@ describe('numberInput()', () =>
777
815
  {
778
816
  it('default', () =>
779
817
  {
780
- expect(numberInput({}))
818
+ expect(numberInput(stringH, {}))
781
819
  .toBe('<div class="sk-number-input-wrapper">'
782
820
  + '<input class="sk-number-input-field" type="number" />'
783
821
  + '</div>');
@@ -785,11 +823,17 @@ describe('numberInput()', () =>
785
823
 
786
824
  it('with min, max, step, name', () =>
787
825
  {
788
- expect(numberInput({ min: '0', max: '100', step: '1', name: 'qty' }))
826
+ expect(numberInput(stringH, { min: '0', max: '100', step: '1', name: 'qty' }))
789
827
  .toBe('<div class="sk-number-input-wrapper">'
790
828
  + '<input class="sk-number-input-field" type="number" min="0" max="100" step="1" name="qty" />'
791
829
  + '</div>');
792
830
  });
831
+
832
+ it('emits corner classes from corners array', () =>
833
+ {
834
+ const html = numberInput(stringH, { corners: [ 'bottom-left' ] });
835
+ expect(html).toContain('sk-cut-bottom-left');
836
+ });
793
837
  });
794
838
 
795
839
  //----------------------------------------------------------------------------------------------------------------------
@@ -798,13 +842,13 @@ describe('tagsInput()', () =>
798
842
  {
799
843
  it('default empty', () =>
800
844
  {
801
- expect(tagsInput({}))
845
+ expect(tagsInput(stringH, {}))
802
846
  .toBe('<div class="sk-tags-input"></div>');
803
847
  });
804
848
 
805
849
  it('with tags', () =>
806
850
  {
807
- expect(tagsInput({}, '<span class="sk-tags-input-tag">foo</span>'))
851
+ expect(tagsInput(stringH, {}, '<span class="sk-tags-input-tag">foo</span>'))
808
852
  .toBe('<div class="sk-tags-input"><span class="sk-tags-input-tag">foo</span></div>');
809
853
  });
810
854
  });
@@ -817,21 +861,69 @@ describe('dropdown()', () =>
817
861
  {
818
862
  it('default', () =>
819
863
  {
820
- expect(dropdown({ summary: 'Actions' }, '<a href="#">Item 1</a>'))
864
+ expect(dropdown(stringH, { summary: 'Actions' }, '<a href="#">Item 1</a>'))
821
865
  .toBe('<details class="sk-dropdown"><summary>Actions</summary><a href="#">Item 1</a></details>');
822
866
  });
823
867
 
824
868
  it('open attribute when props.open is true', () =>
825
869
  {
826
- expect(dropdown({ summary: 'x', open: true }, 'content'))
870
+ expect(dropdown(stringH, { summary: 'x', open: true }, 'content'))
827
871
  .toBe('<details class="sk-dropdown" open><summary>x</summary>content</details>');
828
872
  });
829
873
 
830
874
  it('kind + size', () =>
831
875
  {
832
- expect(dropdown({ summary: 'x', kind: 'primary', size: 'md' }, 'content'))
876
+ expect(dropdown(stringH, { summary: 'x', kind: 'primary', size: 'md' }, 'content'))
833
877
  .toBe('<details class="sk-dropdown sk-primary sk-md sk-size-md"><summary>x</summary>content</details>');
834
878
  });
835
879
  });
836
880
 
837
881
  //----------------------------------------------------------------------------------------------------------------------
882
+ // Task 10 (InputGroup): InputGroup + InputGroupAddon helpers
883
+ //----------------------------------------------------------------------------------------------------------------------
884
+
885
+ describe('inputGroup()', () =>
886
+ {
887
+ it('default', () =>
888
+ {
889
+ expect(inputGroup(stringH, {}, 'x')).toBe('<div class="sk-input-group">x</div>');
890
+ });
891
+
892
+ it('emits corner classes from corners array', () =>
893
+ {
894
+ const html = inputGroup(stringH, { corners: [ 'top-left', 'bottom-right' ] }, 'x');
895
+ expect(html).toContain('sk-cut-top-left');
896
+ expect(html).toContain('sk-cut-bottom-right');
897
+ });
898
+
899
+ it('size variant emits both sk-XX and sk-size-XX', () =>
900
+ {
901
+ expect(inputGroup(stringH, { size: 'lg' }, 'x'))
902
+ .toBe('<div class="sk-input-group sk-lg sk-size-lg">x</div>');
903
+ });
904
+
905
+ it('custom colors emit inline style', () =>
906
+ {
907
+ expect(inputGroup(stringH, { baseColor: '#f00', textColor: '#fff' }, 'x')).toBe(
908
+ '<div class="sk-input-group" style="--sk-input-group-color-base: #f00; --sk-input-group-fg: #fff;">x</div>'
909
+ );
910
+ });
911
+ });
912
+
913
+ //----------------------------------------------------------------------------------------------------------------------
914
+
915
+ describe('inputGroupAddon()', () =>
916
+ {
917
+ it('default', () =>
918
+ {
919
+ expect(inputGroupAddon(stringH, {}, '$')).toBe('<span class="sk-input-group-addon">$</span>');
920
+ });
921
+
922
+ it('size + kind', () =>
923
+ {
924
+ expect(inputGroupAddon(stringH, { size: 'sm', kind: 'primary' }, '$'))
925
+ .toBe('<span class="sk-input-group-addon sk-primary sk-sm sk-size-sm">$</span>');
926
+ });
927
+ });
928
+
929
+ //----------------------------------------------------------------------------------------------------------------------