ngwr 10.2.0 → 11.0.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 (471) hide show
  1. package/README.md +39 -19
  2. package/anchor/styles/_index.scss +4 -4
  3. package/back-top/styles/_index.scss +6 -1
  4. package/blur-text/styles/_index.scss +13 -0
  5. package/button/styles/_index.scss +11 -6
  6. package/carousel/styles/_index.scss +13 -2
  7. package/cascader/styles/_index.scss +2 -2
  8. package/circular-text/styles/_index.scss +2 -0
  9. package/collapse/styles/_index.scss +1 -1
  10. package/color-picker/styles/_index.scss +6 -1
  11. package/compare/styles/_index.scss +1 -2
  12. package/context-menu/styles/_index.scss +6 -1
  13. package/descriptions/styles/_index.scss +2 -2
  14. package/dialog/styles/_index.scss +38 -0
  15. package/drawer/styles/_index.scss +20 -1
  16. package/dropdown/styles/_index.scss +19 -5
  17. package/empty/styles/_index.scss +10 -0
  18. package/fesm2022/ngwr-action-sheet.mjs +15 -5
  19. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  20. package/fesm2022/ngwr-affix.mjs +43 -16
  21. package/fesm2022/ngwr-affix.mjs.map +1 -1
  22. package/fesm2022/ngwr-alert.mjs +3 -3
  23. package/fesm2022/ngwr-anchor.mjs +20 -8
  24. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  25. package/fesm2022/ngwr-aurora.mjs +4 -4
  26. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  27. package/fesm2022/ngwr-avatar.mjs +33 -9
  28. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  29. package/fesm2022/ngwr-back-top.mjs +8 -4
  30. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  31. package/fesm2022/ngwr-badge.mjs +6 -6
  32. package/fesm2022/ngwr-badge.mjs.map +1 -1
  33. package/fesm2022/ngwr-bar-chart.mjs +32 -11
  34. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  35. package/fesm2022/ngwr-blur-text.mjs +4 -4
  36. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  37. package/fesm2022/ngwr-border-glow.mjs +4 -4
  38. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  39. package/fesm2022/ngwr-breadcrumbs.mjs +9 -8
  40. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  41. package/fesm2022/ngwr-burger.mjs +11 -6
  42. package/fesm2022/ngwr-burger.mjs.map +1 -1
  43. package/fesm2022/ngwr-button-testing.mjs +96 -0
  44. package/fesm2022/ngwr-button-testing.mjs.map +1 -0
  45. package/fesm2022/ngwr-button.mjs +89 -24
  46. package/fesm2022/ngwr-button.mjs.map +1 -1
  47. package/fesm2022/ngwr-calendar-heatmap.mjs +42 -10
  48. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  49. package/fesm2022/ngwr-calendar.mjs +108 -9
  50. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  51. package/fesm2022/ngwr-card.mjs +10 -10
  52. package/fesm2022/ngwr-card.mjs.map +1 -1
  53. package/fesm2022/ngwr-carousel-testing.mjs +377 -0
  54. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
  55. package/fesm2022/ngwr-carousel.mjs +72 -18
  56. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  57. package/fesm2022/ngwr-cascader-testing.mjs +534 -0
  58. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -0
  59. package/fesm2022/ngwr-cascader.mjs +4 -4
  60. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  61. package/fesm2022/ngwr-checkbox-testing.mjs +90 -0
  62. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -0
  63. package/fesm2022/ngwr-checkbox.mjs +15 -9
  64. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  65. package/fesm2022/ngwr-circular-text.mjs +4 -4
  66. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  67. package/fesm2022/ngwr-click-spark.mjs +4 -4
  68. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  69. package/fesm2022/ngwr-clipboard.mjs +18 -4
  70. package/fesm2022/ngwr-clipboard.mjs.map +1 -1
  71. package/fesm2022/ngwr-collapse-testing.mjs +368 -0
  72. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
  73. package/fesm2022/ngwr-collapse.mjs +9 -9
  74. package/fesm2022/ngwr-color-picker.mjs +139 -19
  75. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  76. package/fesm2022/ngwr-command-palette-testing.mjs +589 -0
  77. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -0
  78. package/fesm2022/ngwr-command-palette.mjs +60 -10
  79. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  80. package/fesm2022/ngwr-compare.mjs +105 -15
  81. package/fesm2022/ngwr-compare.mjs.map +1 -1
  82. package/fesm2022/ngwr-confetti.mjs +3 -3
  83. package/fesm2022/ngwr-config.mjs +106 -0
  84. package/fesm2022/ngwr-config.mjs.map +1 -0
  85. package/fesm2022/ngwr-context-menu-testing.mjs +631 -0
  86. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -0
  87. package/fesm2022/ngwr-context-menu.mjs +104 -18
  88. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  89. package/fesm2022/ngwr-cookie.mjs +8 -4
  90. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  91. package/fesm2022/ngwr-counter.mjs +17 -9
  92. package/fesm2022/ngwr-counter.mjs.map +1 -1
  93. package/fesm2022/ngwr-date-adapter-fns.mjs +3 -3
  94. package/fesm2022/ngwr-date-adapter-luxon.mjs +55 -7
  95. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
  96. package/fesm2022/ngwr-date-adapter.mjs +57 -11
  97. package/fesm2022/ngwr-date-adapter.mjs.map +1 -1
  98. package/fesm2022/ngwr-date-picker-testing.mjs +766 -0
  99. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -0
  100. package/fesm2022/ngwr-date-picker.mjs +409 -57
  101. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  102. package/fesm2022/ngwr-decrypt-text.mjs +30 -12
  103. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  104. package/fesm2022/ngwr-density.mjs +6 -6
  105. package/fesm2022/ngwr-descriptions.mjs +6 -6
  106. package/fesm2022/ngwr-dialog-testing.mjs +89 -0
  107. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
  108. package/fesm2022/ngwr-dialog.mjs +33 -17
  109. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  110. package/fesm2022/ngwr-directives.mjs +48 -24
  111. package/fesm2022/ngwr-directives.mjs.map +1 -1
  112. package/fesm2022/ngwr-divider.mjs +3 -3
  113. package/fesm2022/ngwr-donut-chart.mjs +24 -7
  114. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  115. package/fesm2022/ngwr-drag-drop.mjs +7 -7
  116. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  117. package/fesm2022/ngwr-drawer-testing.mjs +250 -0
  118. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -0
  119. package/fesm2022/ngwr-drawer.mjs +37 -19
  120. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  121. package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
  122. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
  123. package/fesm2022/ngwr-dropdown.mjs +19 -11
  124. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  125. package/fesm2022/ngwr-empty.mjs +26 -9
  126. package/fesm2022/ngwr-empty.mjs.map +1 -1
  127. package/fesm2022/ngwr-event-calendar.mjs +62 -15
  128. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  129. package/fesm2022/ngwr-falling-text.mjs +4 -4
  130. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  131. package/fesm2022/ngwr-file-upload-testing.mjs +332 -0
  132. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-file-upload.mjs +3 -3
  134. package/fesm2022/ngwr-form-testing.mjs +451 -0
  135. package/fesm2022/ngwr-form-testing.mjs.map +1 -0
  136. package/fesm2022/ngwr-form.mjs +28 -12
  137. package/fesm2022/ngwr-form.mjs.map +1 -1
  138. package/fesm2022/ngwr-fuzzy-text.mjs +11 -4
  139. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  140. package/fesm2022/ngwr-gauge.mjs +21 -7
  141. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  142. package/fesm2022/ngwr-glitch-text.mjs +4 -4
  143. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  144. package/fesm2022/ngwr-gradient-text.mjs +4 -4
  145. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  146. package/fesm2022/ngwr-hotkey.mjs +31 -10
  147. package/fesm2022/ngwr-hotkey.mjs.map +1 -1
  148. package/fesm2022/ngwr-i18n-en.mjs +61 -0
  149. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  150. package/fesm2022/ngwr-i18n-ru.mjs +59 -0
  151. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  152. package/fesm2022/ngwr-i18n.mjs +9 -9
  153. package/fesm2022/ngwr-icon.mjs +6 -6
  154. package/fesm2022/ngwr-image-cropper.mjs +26 -3
  155. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  156. package/fesm2022/ngwr-input-number-testing.mjs +306 -0
  157. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -0
  158. package/fesm2022/ngwr-input-number.mjs +37 -5
  159. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  160. package/fesm2022/ngwr-input-otp-testing.mjs +419 -0
  161. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -0
  162. package/fesm2022/ngwr-input-otp.mjs +39 -8
  163. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  164. package/fesm2022/ngwr-input-testing.mjs +102 -0
  165. package/fesm2022/ngwr-input-testing.mjs.map +1 -0
  166. package/fesm2022/ngwr-input.mjs +62 -25
  167. package/fesm2022/ngwr-input.mjs.map +1 -1
  168. package/fesm2022/ngwr-keyboard.mjs +3 -3
  169. package/fesm2022/ngwr-knob.mjs +46 -10
  170. package/fesm2022/ngwr-knob.mjs.map +1 -1
  171. package/fesm2022/ngwr-layout.mjs +36 -20
  172. package/fesm2022/ngwr-layout.mjs.map +1 -1
  173. package/fesm2022/ngwr-lightbox.mjs +35 -8
  174. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  175. package/fesm2022/ngwr-line-chart.mjs +19 -6
  176. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  177. package/fesm2022/ngwr-list.mjs +14 -9
  178. package/fesm2022/ngwr-list.mjs.map +1 -1
  179. package/fesm2022/ngwr-loading-bar.mjs +71 -31
  180. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  181. package/fesm2022/ngwr-markdown-testing.mjs +516 -0
  182. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -0
  183. package/fesm2022/ngwr-markdown.mjs +1492 -0
  184. package/fesm2022/ngwr-markdown.mjs.map +1 -0
  185. package/fesm2022/ngwr-marquee.mjs +19 -8
  186. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  187. package/fesm2022/ngwr-media.mjs +3 -3
  188. package/fesm2022/ngwr-mention-testing.mjs +513 -0
  189. package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
  190. package/fesm2022/ngwr-mention.mjs +16 -7
  191. package/fesm2022/ngwr-mention.mjs.map +1 -1
  192. package/fesm2022/ngwr-meta.mjs +28 -13
  193. package/fesm2022/ngwr-meta.mjs.map +1 -1
  194. package/fesm2022/ngwr-meter-group.mjs +22 -8
  195. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  196. package/fesm2022/ngwr-overlay.mjs +57 -8
  197. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  198. package/fesm2022/ngwr-page-header.mjs +3 -3
  199. package/fesm2022/ngwr-pagination-testing.mjs +389 -0
  200. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
  201. package/fesm2022/ngwr-pagination.mjs +27 -11
  202. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  203. package/fesm2022/ngwr-pipes.mjs +21 -21
  204. package/fesm2022/ngwr-platform.mjs +9 -9
  205. package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
  206. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
  207. package/fesm2022/ngwr-popconfirm.mjs +99 -21
  208. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  209. package/fesm2022/ngwr-popover-testing.mjs +316 -0
  210. package/fesm2022/ngwr-popover-testing.mjs.map +1 -0
  211. package/fesm2022/ngwr-popover.mjs +48 -10
  212. package/fesm2022/ngwr-popover.mjs.map +1 -1
  213. package/fesm2022/ngwr-progress.mjs +3 -3
  214. package/fesm2022/ngwr-pull-to-refresh.mjs +19 -4
  215. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  216. package/fesm2022/ngwr-qr.mjs +15 -5
  217. package/fesm2022/ngwr-qr.mjs.map +1 -1
  218. package/fesm2022/ngwr-radio-testing.mjs +392 -0
  219. package/fesm2022/ngwr-radio-testing.mjs.map +1 -0
  220. package/fesm2022/ngwr-radio.mjs +16 -9
  221. package/fesm2022/ngwr-radio.mjs.map +1 -1
  222. package/fesm2022/ngwr-rating-testing.mjs +384 -0
  223. package/fesm2022/ngwr-rating-testing.mjs.map +1 -0
  224. package/fesm2022/ngwr-rating.mjs +40 -5
  225. package/fesm2022/ngwr-rating.mjs.map +1 -1
  226. package/fesm2022/ngwr-result.mjs +12 -12
  227. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  228. package/fesm2022/ngwr-scroll.mjs +3 -3
  229. package/fesm2022/ngwr-segmented-testing.mjs +512 -0
  230. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
  231. package/fesm2022/ngwr-segmented.mjs +3 -3
  232. package/fesm2022/ngwr-select-testing.mjs +268 -0
  233. package/fesm2022/ngwr-select-testing.mjs.map +1 -0
  234. package/fesm2022/ngwr-select.mjs +96 -25
  235. package/fesm2022/ngwr-select.mjs.map +1 -1
  236. package/fesm2022/ngwr-shiny-text.mjs +7 -7
  237. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  238. package/fesm2022/ngwr-sidebar.mjs +67 -14
  239. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  240. package/fesm2022/ngwr-skeleton.mjs +3 -3
  241. package/fesm2022/ngwr-slider-testing.mjs +478 -0
  242. package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
  243. package/fesm2022/ngwr-slider.mjs +59 -15
  244. package/fesm2022/ngwr-slider.mjs.map +1 -1
  245. package/fesm2022/ngwr-sparkline.mjs +28 -8
  246. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  247. package/fesm2022/ngwr-speed-dial.mjs +39 -6
  248. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  249. package/fesm2022/ngwr-spinner.mjs +14 -5
  250. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  251. package/fesm2022/ngwr-splash-cursor.mjs +4 -4
  252. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  253. package/fesm2022/ngwr-split-text.mjs +4 -4
  254. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  255. package/fesm2022/ngwr-splitter.mjs +72 -11
  256. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  257. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  258. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  259. package/fesm2022/ngwr-squircle.mjs +6 -6
  260. package/fesm2022/ngwr-star-border.mjs +4 -4
  261. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  262. package/fesm2022/ngwr-statistic.mjs +33 -16
  263. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  264. package/fesm2022/ngwr-stepper-testing.mjs +477 -0
  265. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -0
  266. package/fesm2022/ngwr-stepper.mjs +6 -6
  267. package/fesm2022/ngwr-storage.mjs +27 -17
  268. package/fesm2022/ngwr-storage.mjs.map +1 -1
  269. package/fesm2022/ngwr-switch-testing.mjs +77 -0
  270. package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
  271. package/fesm2022/ngwr-switch.mjs +12 -6
  272. package/fesm2022/ngwr-switch.mjs.map +1 -1
  273. package/fesm2022/ngwr-table-testing.mjs +580 -0
  274. package/fesm2022/ngwr-table-testing.mjs.map +1 -0
  275. package/fesm2022/ngwr-table.mjs +105 -44
  276. package/fesm2022/ngwr-table.mjs.map +1 -1
  277. package/fesm2022/ngwr-tabs-testing.mjs +617 -0
  278. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -0
  279. package/fesm2022/ngwr-tabs.mjs +44 -12
  280. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  281. package/fesm2022/ngwr-textarea-testing.mjs +238 -0
  282. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -0
  283. package/fesm2022/ngwr-textarea.mjs +35 -7
  284. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  285. package/fesm2022/ngwr-theme.mjs +3 -3
  286. package/fesm2022/ngwr-tilt-card.mjs +7 -7
  287. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  288. package/fesm2022/ngwr-timeline.mjs +6 -6
  289. package/fesm2022/ngwr-toast-testing.mjs +101 -0
  290. package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
  291. package/fesm2022/ngwr-toast.mjs +55 -21
  292. package/fesm2022/ngwr-toast.mjs.map +1 -1
  293. package/fesm2022/ngwr-toolbar.mjs +3 -3
  294. package/fesm2022/ngwr-tour.mjs +67 -15
  295. package/fesm2022/ngwr-tour.mjs.map +1 -1
  296. package/fesm2022/ngwr-transfer-testing.mjs +538 -0
  297. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -0
  298. package/fesm2022/ngwr-transfer.mjs +47 -16
  299. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  300. package/fesm2022/ngwr-tree-testing.mjs +734 -0
  301. package/fesm2022/ngwr-tree-testing.mjs.map +1 -0
  302. package/fesm2022/ngwr-tree.mjs +75 -9
  303. package/fesm2022/ngwr-tree.mjs.map +1 -1
  304. package/fesm2022/ngwr-typewriter.mjs +7 -5
  305. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  306. package/fesm2022/ngwr-typography.mjs +3 -3
  307. package/fesm2022/ngwr-version.mjs +1 -1
  308. package/fesm2022/ngwr-version.mjs.map +1 -1
  309. package/fesm2022/ngwr-virtual-scroll.mjs +33 -7
  310. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  311. package/fesm2022/ngwr-waves.mjs +5 -5
  312. package/fesm2022/ngwr-waves.mjs.map +1 -1
  313. package/fesm2022/ngwr-window.mjs +88 -18
  314. package/fesm2022/ngwr-window.mjs.map +1 -1
  315. package/gauge/styles/_index.scss +1 -1
  316. package/glitch-text/styles/_index.scss +4 -0
  317. package/i18n/en.json +56 -0
  318. package/i18n/ru.json +56 -0
  319. package/image-cropper/styles/_index.scss +13 -1
  320. package/input/styles/_index.scss +2 -2
  321. package/input-number/styles/_index.scss +7 -2
  322. package/knob/styles/_index.scss +8 -1
  323. package/layout/styles/_index.scss +3 -3
  324. package/lightbox/styles/_index.scss +10 -2
  325. package/line-chart/styles/_index.scss +6 -0
  326. package/llms-full.txt +151 -4
  327. package/llms.txt +57 -8
  328. package/loading-bar/styles/_index.scss +1 -2
  329. package/markdown/styles/_index.scss +296 -0
  330. package/marquee/styles/_index.scss +12 -0
  331. package/mcp/api.js +512 -0
  332. package/mcp/catalog.js +157 -0
  333. package/mcp/server.js +273 -0
  334. package/mcp/tools.js +366 -0
  335. package/meter-group/styles/_index.scss +1 -1
  336. package/package.json +186 -35
  337. package/pagination/styles/_index.scss +6 -6
  338. package/popover/styles/_index.scss +37 -5
  339. package/rating/styles/_index.scss +10 -0
  340. package/schematics/use/symbol-map.json +2 -0
  341. package/segmented/styles/_index.scss +7 -0
  342. package/select/styles/_index.scss +29 -9
  343. package/sidebar/styles/_index.scss +30 -5
  344. package/slider/styles/_index.scss +15 -0
  345. package/speed-dial/styles/_index.scss +20 -0
  346. package/split-text/styles/_index.scss +13 -0
  347. package/star-border/styles/_index.scss +2 -0
  348. package/stepper/styles/_index.scss +1 -1
  349. package/styles/_grid.scss +4 -4
  350. package/styles.scss +1 -0
  351. package/switch/styles/_index.scss +5 -3
  352. package/table/styles/_index.scss +22 -11
  353. package/tabs/styles/_index.scss +11 -1
  354. package/textarea/styles/_index.scss +5 -0
  355. package/theme/styles/_colors.scss +29 -13
  356. package/theme/styles/_dark.scss +11 -3
  357. package/theme/styles/_mixins.scss +3 -0
  358. package/timeline/styles/_index.scss +2 -2
  359. package/toast/styles/_index.scss +17 -8
  360. package/toolbar/styles/_index.scss +3 -3
  361. package/tour/styles/_index.scss +7 -1
  362. package/tree/styles/_index.scss +5 -3
  363. package/types/ngwr-action-sheet.d.ts +9 -1
  364. package/types/ngwr-affix.d.ts +7 -1
  365. package/types/ngwr-anchor.d.ts +8 -1
  366. package/types/ngwr-avatar.d.ts +13 -3
  367. package/types/ngwr-bar-chart.d.ts +16 -0
  368. package/types/ngwr-burger.d.ts +6 -2
  369. package/types/ngwr-button-testing.d.ts +85 -0
  370. package/types/ngwr-button.d.ts +58 -10
  371. package/types/ngwr-calendar-heatmap.d.ts +16 -2
  372. package/types/ngwr-calendar.d.ts +37 -1
  373. package/types/ngwr-carousel-testing.d.ts +257 -0
  374. package/types/ngwr-carousel.d.ts +20 -4
  375. package/types/ngwr-cascader-testing.d.ts +392 -0
  376. package/types/ngwr-checkbox-testing.d.ts +77 -0
  377. package/types/ngwr-checkbox.d.ts +7 -2
  378. package/types/ngwr-clipboard.d.ts +8 -1
  379. package/types/ngwr-collapse-testing.d.ts +290 -0
  380. package/types/ngwr-color-picker.d.ts +20 -11
  381. package/types/ngwr-command-palette-testing.d.ts +437 -0
  382. package/types/ngwr-command-palette.d.ts +25 -2
  383. package/types/ngwr-compare.d.ts +50 -2
  384. package/types/ngwr-config.d.ts +176 -0
  385. package/types/ngwr-context-menu-testing.d.ts +401 -0
  386. package/types/ngwr-context-menu.d.ts +65 -17
  387. package/types/ngwr-counter.d.ts +6 -1
  388. package/types/ngwr-date-adapter-luxon.d.ts +26 -0
  389. package/types/ngwr-date-picker-testing.d.ts +518 -0
  390. package/types/ngwr-date-picker.d.ts +128 -8
  391. package/types/ngwr-dialog-testing.d.ts +79 -0
  392. package/types/ngwr-dialog.d.ts +13 -0
  393. package/types/ngwr-directives.d.ts +10 -1
  394. package/types/ngwr-donut-chart.d.ts +8 -1
  395. package/types/ngwr-drawer-testing.d.ts +196 -0
  396. package/types/ngwr-drawer.d.ts +15 -0
  397. package/types/ngwr-dropdown-testing.d.ts +202 -0
  398. package/types/ngwr-dropdown.d.ts +7 -1
  399. package/types/ngwr-empty.d.ts +19 -4
  400. package/types/ngwr-event-calendar.d.ts +26 -1
  401. package/types/ngwr-file-upload-testing.d.ts +194 -0
  402. package/types/ngwr-form-testing.d.ts +379 -0
  403. package/types/ngwr-form.d.ts +17 -0
  404. package/types/ngwr-gauge.d.ts +13 -1
  405. package/types/ngwr-hotkey.d.ts +2 -0
  406. package/types/ngwr-image-cropper.d.ts +10 -0
  407. package/types/ngwr-input-number-testing.d.ts +226 -0
  408. package/types/ngwr-input-number.d.ts +29 -1
  409. package/types/ngwr-input-otp-testing.d.ts +299 -0
  410. package/types/ngwr-input-otp.d.ts +23 -2
  411. package/types/ngwr-input-testing.d.ts +84 -0
  412. package/types/ngwr-input.d.ts +20 -6
  413. package/types/ngwr-knob.d.ts +18 -0
  414. package/types/ngwr-layout.d.ts +1 -0
  415. package/types/ngwr-lightbox.d.ts +17 -3
  416. package/types/ngwr-line-chart.d.ts +9 -2
  417. package/types/ngwr-list.d.ts +7 -2
  418. package/types/ngwr-loading-bar.d.ts +4 -7
  419. package/types/ngwr-markdown-testing.d.ts +462 -0
  420. package/types/ngwr-markdown.d.ts +534 -0
  421. package/types/ngwr-marquee.d.ts +12 -2
  422. package/types/ngwr-mention-testing.d.ts +359 -0
  423. package/types/ngwr-meta.d.ts +4 -2
  424. package/types/ngwr-meter-group.d.ts +11 -1
  425. package/types/ngwr-overlay.d.ts +33 -2
  426. package/types/ngwr-pagination-testing.d.ts +291 -0
  427. package/types/ngwr-pagination.d.ts +7 -1
  428. package/types/ngwr-popconfirm-testing.d.ts +235 -0
  429. package/types/ngwr-popconfirm.d.ts +31 -21
  430. package/types/ngwr-popover-testing.d.ts +221 -0
  431. package/types/ngwr-popover.d.ts +19 -2
  432. package/types/ngwr-pull-to-refresh.d.ts +7 -0
  433. package/types/ngwr-qr.d.ts +9 -1
  434. package/types/ngwr-radio-testing.d.ts +296 -0
  435. package/types/ngwr-radio.d.ts +8 -2
  436. package/types/ngwr-rating-testing.d.ts +291 -0
  437. package/types/ngwr-rating.d.ts +23 -0
  438. package/types/ngwr-segmented-testing.d.ts +401 -0
  439. package/types/ngwr-select-testing.d.ts +175 -0
  440. package/types/ngwr-select.d.ts +45 -4
  441. package/types/ngwr-sidebar.d.ts +17 -2
  442. package/types/ngwr-slider-testing.d.ts +329 -0
  443. package/types/ngwr-slider.d.ts +35 -1
  444. package/types/ngwr-sparkline.d.ts +12 -2
  445. package/types/ngwr-speed-dial.d.ts +17 -18
  446. package/types/ngwr-spinner.d.ts +8 -1
  447. package/types/ngwr-splitter.d.ts +24 -1
  448. package/types/ngwr-statistic.d.ts +12 -2
  449. package/types/ngwr-stepper-testing.d.ts +346 -0
  450. package/types/ngwr-storage.d.ts +10 -1
  451. package/types/ngwr-switch-testing.d.ts +66 -0
  452. package/types/ngwr-switch.d.ts +7 -2
  453. package/types/ngwr-table-testing.d.ts +423 -0
  454. package/types/ngwr-table.d.ts +42 -0
  455. package/types/ngwr-tabs-testing.d.ts +470 -0
  456. package/types/ngwr-tabs.d.ts +9 -0
  457. package/types/ngwr-textarea-testing.d.ts +184 -0
  458. package/types/ngwr-textarea.d.ts +7 -2
  459. package/types/ngwr-toast-testing.d.ts +80 -0
  460. package/types/ngwr-toast.d.ts +19 -4
  461. package/types/ngwr-tour.d.ts +27 -0
  462. package/types/ngwr-transfer-testing.d.ts +444 -0
  463. package/types/ngwr-transfer.d.ts +16 -1
  464. package/types/ngwr-tree-testing.d.ts +501 -0
  465. package/types/ngwr-tree.d.ts +33 -0
  466. package/types/ngwr-version.d.ts +1 -1
  467. package/types/ngwr-virtual-scroll.d.ts +13 -1
  468. package/types/ngwr-window.d.ts +23 -2
  469. package/typewriter/styles/_index.scss +1 -1
  470. package/typography/styles/_index.scss +1 -1
  471. package/window/styles/_index.scss +32 -9
@@ -0,0 +1,451 @@
1
+ import { ContentContainerComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+ /**
10
+ * The messages this field is showing, or the ones it is holding back.
11
+ *
12
+ * Anchored with `:scope >` because the field's own parts are all direct children
13
+ * of its host: a `<wr-form-field>` nested in another one's control slot otherwise
14
+ * has its error block matched by the OUTER field's query too, and the outer
15
+ * answers with a sentence that is not its own.
16
+ */
17
+ const MESSAGE = ':scope > .wr-form-field__errors > .wr-form-field__error';
18
+ const VISIBLE = `${MESSAGE}:not(.wr-form-field__error--hidden)`;
19
+ const SUPPRESSED = `${MESSAGE}.wr-form-field__error--hidden`;
20
+ /** The control slot — every native control lookup is scoped through this. */
21
+ const SLOT = ':scope > .wr-form-field__control';
22
+ /** The label and the hint, anchored for the same reason as {@link MESSAGE}. */
23
+ const LABEL = ':scope > .wr-form-field__label';
24
+ const HINT = ':scope > .wr-form-field__hint';
25
+ /**
26
+ * Test harness for `<wr-form-field>` — the label + control + hint + validation
27
+ * copy block.
28
+ *
29
+ * The field's real job is the COPY CONTRACT, and this harness is built around it.
30
+ * A message reaches the DOM one of two ways, and they are distinguishable there:
31
+ *
32
+ * - a `<wr-form-error key="…">` the consumer wrote, which renders only while that
33
+ * key is failing — the others stay in the DOM carrying
34
+ * `wr-form-field__error--hidden`, which is why {@link getErrors} filters and
35
+ * {@link getSuppressedErrorKeys} exists. A raw `.wr-form-error` query answers
36
+ * with the held-back copy too, and reads green on the wrong sentence.
37
+ * - a message the field RESOLVED for an error no markup claimed:
38
+ * `provideWrFormErrors()`, then the `ngwr/i18n` `validation.*` catalog, then a
39
+ * built-in English sentence. All three render the identical `<div>`, so
40
+ * `source` is `'resolved'` for every one of them — the DOM genuinely cannot say
41
+ * which link answered, and this harness does not pretend otherwise.
42
+ *
43
+ * Nothing renders at all until the projected control is touched or dirty, so a
44
+ * spec has to reach that gate first: {@link blurControl} is the shortest way
45
+ * (blur is what marks a bound control touched), and typing through the control's
46
+ * own harness is the other.
47
+ *
48
+ * The field publishes no `aria-label` anywhere. Its `<label for>` pointing at the
49
+ * projected control IS the naming story, and a control that does not adopt the
50
+ * id leaves the field silently unlabelled — {@link isLabelLinkedToControl} is
51
+ * what catches that. The other half is the reverse wiring: the control reads back
52
+ * `aria-describedby` and `aria-invalid`, which is the only reason a screen reader
53
+ * hears the message ({@link getAnnouncedDescription}).
54
+ *
55
+ * Harnesses loaded through this one resolve INSIDE the control slot, so the
56
+ * control's own harness composes without a selector:
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const email = await loader.getHarness(WrFormFieldHarness.with({ label: 'Email' }));
61
+ *
62
+ * await (await email.getHarness(WrInputHarness)).setValue('nope');
63
+ * await email.blurControl();
64
+ *
65
+ * expect(await email.getErrorTexts()).toEqual(["That isn't a valid email."]);
66
+ * expect(await email.getAnnouncedDescription()).toBe("That isn't a valid email.");
67
+ * ```
68
+ *
69
+ * @see https://ngwr.dev/guides/testing
70
+ */
71
+ class WrFormFieldHarness extends ContentContainerComponentHarness {
72
+ static hostSelector = 'wr-form-field';
73
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
74
+ static with(options = {}) {
75
+ return new HarnessPredicate(WrFormFieldHarness, options)
76
+ .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
77
+ .addOption('hint', options.hint, (harness, hint) => HarnessPredicate.stringMatches(harness.getHint(), hint))
78
+ .addOption('required', options.required, async (harness, required) => (await harness.isRequired()) === required)
79
+ .addOption('invalid', options.invalid, async (harness, invalid) => (await harness.isInvalid()) === invalid)
80
+ .addOption('errorText', options.errorText, async (harness, text) => {
81
+ for (const shown of await harness.getErrorTexts()) {
82
+ if (await HarnessPredicate.stringMatches(shown, text))
83
+ return true;
84
+ }
85
+ return false;
86
+ });
87
+ }
88
+ /**
89
+ * The label text, or `null` when the field renders no label.
90
+ *
91
+ * The `*` and `(optional)` markers ride INSIDE the `<label>`, so they are
92
+ * excluded here — otherwise this answers `'Email *'` and every equality
93
+ * assertion has to know which marker the field happens to carry.
94
+ *
95
+ * `null` is not "the consumer forgot": `label=""` renders no `<label>` element
96
+ * at all, which also means there is nothing naming the control.
97
+ */
98
+ async getLabel() {
99
+ const label = await this.labelElement();
100
+ return label ? label.text({ exclude: '.wr-form-field__required, .wr-form-field__optional' }) : null;
101
+ }
102
+ /**
103
+ * Whether the field is marked required.
104
+ *
105
+ * Read from the host's `wr-form-field--required` modifier, not from the `*`:
106
+ * that marker is `aria-hidden="true"` decoration, and it is only rendered when
107
+ * there is a label to hang it on — the modifier is set either way. Note what
108
+ * this does NOT mean: the field forwards nothing to the projected control, so
109
+ * `required` here is the field's own visual state. What announces the constraint
110
+ * is the forms layer — a `required` rule writes the native `required` attribute
111
+ * onto the control — and the two are set independently, so a field can carry the
112
+ * `*` with no validator behind it.
113
+ */
114
+ async isRequired() {
115
+ return (await this.host()).hasClass('wr-form-field--required');
116
+ }
117
+ /**
118
+ * Whether the label carries the `(optional)` marker.
119
+ *
120
+ * Read from the marker itself, because — unlike `required` — `optional` sets no
121
+ * host modifier. It is also the marker a screen reader actually reads: this one
122
+ * is not `aria-hidden`. `required` wins when both inputs are set, so a required
123
+ * field is never optional here however it was bound.
124
+ */
125
+ async isOptional() {
126
+ return (await this.locatorForOptional(`${LABEL} .wr-form-field__optional`)()) !== null;
127
+ }
128
+ /**
129
+ * The hint text, or `null` when none is showing.
130
+ *
131
+ * `null` does not mean the consumer set no hint: the error block takes the
132
+ * hint's slot outright, so a field with a visible message has no hint in the DOM
133
+ * at all. Note also that the hint carries no id and is NOT referenced by the
134
+ * control's `aria-describedby` — see {@link getAnnouncedDescription}.
135
+ */
136
+ async getHint() {
137
+ const hint = await this.locatorForOptional(HINT)();
138
+ return hint ? hint.text() : null;
139
+ }
140
+ /**
141
+ * Whether the field is painting the error state.
142
+ *
143
+ * The host's `wr-form-field--invalid` modifier — what the user sees. The
144
+ * screen-reader half lives on the control as `aria-invalid`, and the two are
145
+ * separate elements rather than two readings of one: {@link isControlInvalid}.
146
+ */
147
+ async isInvalid() {
148
+ return (await this.host()).hasClass('wr-form-field--invalid');
149
+ }
150
+ /**
151
+ * Every message the field is SHOWING, in DOM order — the consumer's markup
152
+ * first, then whatever the field resolved for the keys that markup left over.
153
+ *
154
+ * Only THIS field's own error block is read — its direct child, holding its
155
+ * direct children — so a `<wr-form-field>` nested in another one's control slot
156
+ * contributes nothing to the outer field's answer. Without that anchoring the
157
+ * outer field reports the inner one's copy as its own, which is the same class
158
+ * of break as reading a sibling field's messages.
159
+ */
160
+ async getErrors() {
161
+ const elements = await this.locatorForAll(VISIBLE)();
162
+ return Promise.all(elements.map(async (element) => ({
163
+ key: await element.getAttribute('data-key'),
164
+ text: await element.text(),
165
+ // The consumer's copy is its own element; a resolved message is a plain
166
+ // `<div>` the field stamped out. That tag name is the whole of what the
167
+ // DOM knows about provenance.
168
+ source: (await element.getProperty('tagName')).toLowerCase() === 'wr-form-error'
169
+ ? 'projected'
170
+ : 'resolved',
171
+ })));
172
+ }
173
+ /** The visible messages as text, in DOM order. */
174
+ async getErrorTexts() {
175
+ return (await this.getErrors()).map(error => error.text);
176
+ }
177
+ /**
178
+ * The visible message for one validator key.
179
+ *
180
+ * Throws rather than answering `null` when that key has no visible message: the
181
+ * two reasons are worth failing on and the message names both — the key is not
182
+ * in error at all, or its copy resolved to nothing.
183
+ */
184
+ async getErrorText(key) {
185
+ const errors = await this.getErrors();
186
+ const match = errors.find(error => error.key === key);
187
+ if (match)
188
+ return match.text;
189
+ const shown = errors.map(error => error.key ?? '(keyless)');
190
+ const detail = shown.length > 0
191
+ ? `Showing: ${shown.join(', ')}.`
192
+ : 'The field is showing nothing — it is either valid, or the control is still untouched and pristine.';
193
+ throw new Error(`WrFormFieldHarness.getErrorText(${JSON.stringify(key)}): no visible message for that key. ${detail}`);
194
+ }
195
+ /**
196
+ * The keys of the projected messages the field is holding BACK — copy that is in
197
+ * the DOM but hidden because its key is not among the failing ones.
198
+ *
199
+ * This is the gating contract seen from outside, and the reason a spec must not
200
+ * query `.wr-form-error` itself: the suppressed sentences sit right next to the
201
+ * live one.
202
+ */
203
+ async getSuppressedErrorKeys() {
204
+ const hidden = await this.locatorForAll(SUPPRESSED)();
205
+ const keys = await Promise.all(hidden.map(element => element.getAttribute('data-key')));
206
+ return keys.filter((key) => key !== null);
207
+ }
208
+ /**
209
+ * Whether the field is announcing an error it has no words for: invalid, with an
210
+ * error block that renders nothing.
211
+ *
212
+ * Worth a named check because it is invisible in review — the field still goes
213
+ * red, and `aria-describedby` still points at the block, so a screen reader is
214
+ * sent to an empty element. It happens when no link in the copy chain answers
215
+ * the key (`[autoErrors]="false"` with no markup, or a key nothing has copy for
216
+ * — Signal Forms reports `minLength`, while the chain only knows Angular's
217
+ * lowercase `minlength`).
218
+ */
219
+ async hasEmptyErrorBlock() {
220
+ if (!(await this.isInvalid()))
221
+ return false;
222
+ return (await this.getErrors()).every(error => error.text.length === 0);
223
+ }
224
+ /** The id the `<label for>` points at, or `null` when there is no label. */
225
+ async getLabelFor() {
226
+ const label = await this.labelElement();
227
+ return label ? label.getAttribute('for') : null;
228
+ }
229
+ /** The `id` the projected control actually answers to, or `null` when it has none. */
230
+ async getControlId() {
231
+ return (await this.control()).getAttribute('id');
232
+ }
233
+ /**
234
+ * Whether the `<label for>` resolves to an element inside THIS field's control
235
+ * slot — the only thing that makes the field nameable.
236
+ *
237
+ * Scoped to the slot on purpose: ids are document-global, so a check that went
238
+ * through `getElementById` would also pass for a label pointing at a DIFFERENT
239
+ * field's control, and for a control that has been moved out of the field
240
+ * entirely. The failure this catches is quiet — a projected control that does
241
+ * not adopt the field's `controlId` (a bare `<input>` with no `[wrInput]`, say)
242
+ * leaves a `for` naming nothing, the field renders exactly as it always did, and
243
+ * nothing is labelled.
244
+ */
245
+ async isLabelLinkedToControl() {
246
+ const id = await this.getLabelFor();
247
+ if (!id)
248
+ return false;
249
+ return (await this.locatorForOptional(`${SLOT} [id="${id}"]`)()) !== null;
250
+ }
251
+ /**
252
+ * The ids the projected control names in `aria-describedby`, in order.
253
+ *
254
+ * The field points this at the error block and nothing else — the hint has no
255
+ * id — so an empty list while a hint is showing is correct rather than a bug.
256
+ * That is not an oversight to fix from a harness either: `[wrInput]` derives
257
+ * `aria-invalid` from the same signal, so describing the hint would announce
258
+ * every hinted field as invalid.
259
+ */
260
+ async getDescribedByIds() {
261
+ const value = await (await this.control()).getAttribute('aria-describedby');
262
+ return value ? value.split(/\s+/).filter(Boolean) : [];
263
+ }
264
+ /**
265
+ * What a screen reader reads as the control's description: the text of the
266
+ * elements `aria-describedby` names, joined by a space. `null` when the control
267
+ * describes nothing.
268
+ *
269
+ * `''` is a real answer and the interesting one — the reference resolves to an
270
+ * element with no text in it, which is {@link hasEmptyErrorBlock}. Ids naming
271
+ * something outside this field are skipped (a consumer's own description is out
272
+ * of a field harness's reach); an id naming nothing anywhere throws, because a
273
+ * dangling reference is the bug this method exists to surface.
274
+ *
275
+ * Suppressed copy is excluded explicitly rather than left to CSS: a browser drops
276
+ * `display: none` subtrees from the accessibility tree, but a unit test loads no
277
+ * stylesheet at all, so the referenced block's raw `textContent` runs every
278
+ * held-back sentence together with the live one and this would answer with copy
279
+ * nobody can read.
280
+ */
281
+ async getAnnouncedDescription() {
282
+ const ids = await this.getDescribedByIds();
283
+ if (ids.length === 0)
284
+ return null;
285
+ const parts = [];
286
+ for (const id of ids) {
287
+ const inField = await this.locatorForOptional(`[id="${id}"]`)();
288
+ if (inField) {
289
+ parts.push(await inField.text({ exclude: '.wr-form-field__error--hidden' }));
290
+ continue;
291
+ }
292
+ if ((await this.documentRootLocatorFactory().locatorForOptional(`[id="${id}"]`)()) === null) {
293
+ throw new Error(`WrFormFieldHarness.getAnnouncedDescription(): the control's aria-describedby names "${id}", which is not ` +
294
+ 'an element anywhere in the document — a screen reader is pointed at nothing.');
295
+ }
296
+ }
297
+ return parts.join(' ');
298
+ }
299
+ /**
300
+ * Whether the control announces itself invalid.
301
+ *
302
+ * `aria-invalid` on the projected control, which is what assistive technology
303
+ * is told — the field's own red styling is {@link isInvalid}. Both are here
304
+ * because they live on different elements and travel in opposite directions: the
305
+ * field renders the message, the control reads back that there is one. A field
306
+ * that paints red while the control says nothing is a real (and silent) break.
307
+ */
308
+ async isControlInvalid() {
309
+ return (await (await this.control()).getAttribute('aria-invalid')) === 'true';
310
+ }
311
+ /** Move focus to the projected control. */
312
+ async focusControl() {
313
+ return (await this.control()).focus();
314
+ }
315
+ /**
316
+ * Blur the projected control, which is usually how a spec gets any message to
317
+ * appear at all: a bound control marks itself touched on blur, and the field
318
+ * renders nothing while the control is untouched AND pristine.
319
+ */
320
+ async blurControl() {
321
+ return (await this.control()).blur();
322
+ }
323
+ /** The rendered `<label>`, or `null` when `label` is empty. */
324
+ async labelElement() {
325
+ return this.locatorForOptional(LABEL)();
326
+ }
327
+ /**
328
+ * The control this field wraps.
329
+ *
330
+ * The element the label points at, when that resolves inside the slot — which
331
+ * covers a control nested inside a component (`<wr-input-number>`'s own
332
+ * `<input>` adopts the field's id, so the label names it, not the component
333
+ * element). Otherwise the first native control in the slot, so a field with no
334
+ * label still answers.
335
+ */
336
+ async control() {
337
+ const id = await this.getLabelFor();
338
+ if (id) {
339
+ const labelled = await this.locatorForOptional(`${SLOT} [id="${id}"]`)();
340
+ if (labelled)
341
+ return labelled;
342
+ }
343
+ const native = await this.locatorForOptional(`${SLOT} input, ${SLOT} select, ${SLOT} textarea`)();
344
+ if (native)
345
+ return native;
346
+ throw new Error('WrFormFieldHarness: this field wraps no control. Nothing in `.wr-form-field__control` answers to the ' +
347
+ "label's `for`, and there is no <input> / <select> / <textarea> to fall back to.");
348
+ }
349
+ /**
350
+ * The content loader every `HarnessLoader` method on this class runs through,
351
+ * scoped to the control slot.
352
+ *
353
+ * So `field.getHarness(WrInputHarness)` finds the control this field wraps and
354
+ * not a sibling field's — the loader is per-instance by construction, which is
355
+ * what a document-wide query for the same selector would lose.
356
+ */
357
+ async getRootHarnessLoader() {
358
+ return this.locatorFactory.harnessLoaderFor(SLOT);
359
+ }
360
+ }
361
+
362
+ /**
363
+ * @license
364
+ *
365
+ * Use of this source code is governed by an MIT-style license that can be
366
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
367
+ */
368
+ /**
369
+ * Test harness for `<wr-form-item>` — the layout-only field row.
370
+ *
371
+ * Deliberately a much smaller surface than {@link WrFormFieldHarness}, because
372
+ * the component is a much smaller thing: it discovers no control, generates no
373
+ * id, wires no `for` and no `aria-describedby`, and reads no validity. It paints
374
+ * the error state it is TOLD to paint, via `[hasError]`, and everything else in
375
+ * it belongs to the consumer — including the `<label>`, which is named here
376
+ * rather than linked (nothing wires it; the consumer's own `for` is the only
377
+ * thing that can).
378
+ *
379
+ * The one behavioural trap it is worth a harness for: a `<wr-form-error>` inside
380
+ * a form-item renders UNCONDITIONALLY. `key` has no effect at all — the gating
381
+ * lives in the surrounding `<wr-form-field>`, and with no field to ask, every
382
+ * message decides it is the one to show. Markup moved from a field into an item
383
+ * therefore starts printing every sentence at once, and {@link getErrorTexts}
384
+ * is what says so.
385
+ *
386
+ * @example
387
+ * ```ts
388
+ * const item = await loader.getHarness(WrFormItemHarness.with({ label: 'Email' }));
389
+ *
390
+ * expect(await item.isInvalid()).toBe(true);
391
+ * expect(await item.getErrorTexts()).toEqual(['Invalid email']);
392
+ * await (await item.getHarness(WrInputHarness)).setValue('ada@example.test');
393
+ * ```
394
+ *
395
+ * @see https://ngwr.dev/guides/testing
396
+ */
397
+ class WrFormItemHarness extends ContentContainerComponentHarness {
398
+ static hostSelector = 'wr-form-item';
399
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
400
+ static with(options = {}) {
401
+ return new HarnessPredicate(WrFormItemHarness, options)
402
+ .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
403
+ .addOption('invalid', options.invalid, async (harness, invalid) => (await harness.isInvalid()) === invalid)
404
+ .addOption('errorText', options.errorText, async (harness, text) => {
405
+ for (const shown of await harness.getErrorTexts()) {
406
+ if (await HarnessPredicate.stringMatches(shown, text))
407
+ return true;
408
+ }
409
+ return false;
410
+ });
411
+ }
412
+ /**
413
+ * The text of the item's own `<label>` child, or `null` when it has none.
414
+ *
415
+ * Direct child only — that is what the item styles (`> label` is what turns red
416
+ * in the error state), so a label nested deeper is the consumer's own business
417
+ * and answering with it would describe something the item never touches.
418
+ */
419
+ async getLabel() {
420
+ const label = await this.locatorForOptional(':scope > label')();
421
+ return label ? label.text() : null;
422
+ }
423
+ /**
424
+ * Whether the item is painting the error state.
425
+ *
426
+ * The `wr-form-item--error` modifier, which is driven entirely by the
427
+ * consumer's `[hasError]` — the item runs no validity check of its own, so this
428
+ * answers what it was told, not what the control thinks.
429
+ */
430
+ async isInvalid() {
431
+ return (await this.host()).hasClass('wr-form-item--error');
432
+ }
433
+ /**
434
+ * Every `<wr-form-error>` in the item, as text, in DOM order.
435
+ *
436
+ * All of them: nothing is gated here, `key` or no `key`. Nothing is ever
437
+ * hidden either, so there is no suppressed-message counterpart to this — see
438
+ * the class docs.
439
+ */
440
+ async getErrorTexts() {
441
+ const errors = await this.locatorForAll('wr-form-error')();
442
+ return Promise.all(errors.map(error => error.text()));
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Generated bundle index. Do not edit.
448
+ */
449
+
450
+ export { WrFormFieldHarness, WrFormItemHarness };
451
+ //# sourceMappingURL=ngwr-form-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-form-testing.mjs","sources":["../../../projects/lib/form/testing/wr-form-field-harness.ts","../../../projects/lib/form/testing/wr-form-item-harness.ts","../../../projects/lib/form/testing/ngwr-form-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ContentContainerComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrFormFieldError, WrFormFieldHarnessFilters } from './interfaces';\n\n/**\n * The messages this field is showing, or the ones it is holding back.\n *\n * Anchored with `:scope >` because the field's own parts are all direct children\n * of its host: a `<wr-form-field>` nested in another one's control slot otherwise\n * has its error block matched by the OUTER field's query too, and the outer\n * answers with a sentence that is not its own.\n */\nconst MESSAGE = ':scope > .wr-form-field__errors > .wr-form-field__error';\nconst VISIBLE = `${MESSAGE}:not(.wr-form-field__error--hidden)`;\nconst SUPPRESSED = `${MESSAGE}.wr-form-field__error--hidden`;\n\n/** The control slot — every native control lookup is scoped through this. */\nconst SLOT = ':scope > .wr-form-field__control';\n\n/** The label and the hint, anchored for the same reason as {@link MESSAGE}. */\nconst LABEL = ':scope > .wr-form-field__label';\nconst HINT = ':scope > .wr-form-field__hint';\n\n/**\n * Test harness for `<wr-form-field>` — the label + control + hint + validation\n * copy block.\n *\n * The field's real job is the COPY CONTRACT, and this harness is built around it.\n * A message reaches the DOM one of two ways, and they are distinguishable there:\n *\n * - a `<wr-form-error key=\"…\">` the consumer wrote, which renders only while that\n * key is failing — the others stay in the DOM carrying\n * `wr-form-field__error--hidden`, which is why {@link getErrors} filters and\n * {@link getSuppressedErrorKeys} exists. A raw `.wr-form-error` query answers\n * with the held-back copy too, and reads green on the wrong sentence.\n * - a message the field RESOLVED for an error no markup claimed:\n * `provideWrFormErrors()`, then the `ngwr/i18n` `validation.*` catalog, then a\n * built-in English sentence. All three render the identical `<div>`, so\n * `source` is `'resolved'` for every one of them — the DOM genuinely cannot say\n * which link answered, and this harness does not pretend otherwise.\n *\n * Nothing renders at all until the projected control is touched or dirty, so a\n * spec has to reach that gate first: {@link blurControl} is the shortest way\n * (blur is what marks a bound control touched), and typing through the control's\n * own harness is the other.\n *\n * The field publishes no `aria-label` anywhere. Its `<label for>` pointing at the\n * projected control IS the naming story, and a control that does not adopt the\n * id leaves the field silently unlabelled — {@link isLabelLinkedToControl} is\n * what catches that. The other half is the reverse wiring: the control reads back\n * `aria-describedby` and `aria-invalid`, which is the only reason a screen reader\n * hears the message ({@link getAnnouncedDescription}).\n *\n * Harnesses loaded through this one resolve INSIDE the control slot, so the\n * control's own harness composes without a selector:\n *\n * @example\n * ```ts\n * const email = await loader.getHarness(WrFormFieldHarness.with({ label: 'Email' }));\n *\n * await (await email.getHarness(WrInputHarness)).setValue('nope');\n * await email.blurControl();\n *\n * expect(await email.getErrorTexts()).toEqual([\"That isn't a valid email.\"]);\n * expect(await email.getAnnouncedDescription()).toBe(\"That isn't a valid email.\");\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrFormFieldHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-form-field';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrFormFieldHarnessFilters = {}): HarnessPredicate<WrFormFieldHarness> {\n return new HarnessPredicate(WrFormFieldHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('hint', options.hint, (harness, hint) => HarnessPredicate.stringMatches(harness.getHint(), hint))\n .addOption('required', options.required, async (harness, required) => (await harness.isRequired()) === required)\n .addOption('invalid', options.invalid, async (harness, invalid) => (await harness.isInvalid()) === invalid)\n .addOption('errorText', options.errorText, async (harness, text) => {\n for (const shown of await harness.getErrorTexts()) {\n if (await HarnessPredicate.stringMatches(shown, text)) return true;\n }\n return false;\n });\n }\n\n /**\n * The label text, or `null` when the field renders no label.\n *\n * The `*` and `(optional)` markers ride INSIDE the `<label>`, so they are\n * excluded here — otherwise this answers `'Email *'` and every equality\n * assertion has to know which marker the field happens to carry.\n *\n * `null` is not \"the consumer forgot\": `label=\"\"` renders no `<label>` element\n * at all, which also means there is nothing naming the control.\n */\n async getLabel(): Promise<string | null> {\n const label = await this.labelElement();\n return label ? label.text({ exclude: '.wr-form-field__required, .wr-form-field__optional' }) : null;\n }\n\n /**\n * Whether the field is marked required.\n *\n * Read from the host's `wr-form-field--required` modifier, not from the `*`:\n * that marker is `aria-hidden=\"true\"` decoration, and it is only rendered when\n * there is a label to hang it on — the modifier is set either way. Note what\n * this does NOT mean: the field forwards nothing to the projected control, so\n * `required` here is the field's own visual state. What announces the constraint\n * is the forms layer — a `required` rule writes the native `required` attribute\n * onto the control — and the two are set independently, so a field can carry the\n * `*` with no validator behind it.\n */\n async isRequired(): Promise<boolean> {\n return (await this.host()).hasClass('wr-form-field--required');\n }\n\n /**\n * Whether the label carries the `(optional)` marker.\n *\n * Read from the marker itself, because — unlike `required` — `optional` sets no\n * host modifier. It is also the marker a screen reader actually reads: this one\n * is not `aria-hidden`. `required` wins when both inputs are set, so a required\n * field is never optional here however it was bound.\n */\n async isOptional(): Promise<boolean> {\n return (await this.locatorForOptional(`${LABEL} .wr-form-field__optional`)()) !== null;\n }\n\n /**\n * The hint text, or `null` when none is showing.\n *\n * `null` does not mean the consumer set no hint: the error block takes the\n * hint's slot outright, so a field with a visible message has no hint in the DOM\n * at all. Note also that the hint carries no id and is NOT referenced by the\n * control's `aria-describedby` — see {@link getAnnouncedDescription}.\n */\n async getHint(): Promise<string | null> {\n const hint = await this.locatorForOptional(HINT)();\n return hint ? hint.text() : null;\n }\n\n /**\n * Whether the field is painting the error state.\n *\n * The host's `wr-form-field--invalid` modifier — what the user sees. The\n * screen-reader half lives on the control as `aria-invalid`, and the two are\n * separate elements rather than two readings of one: {@link isControlInvalid}.\n */\n async isInvalid(): Promise<boolean> {\n return (await this.host()).hasClass('wr-form-field--invalid');\n }\n\n /**\n * Every message the field is SHOWING, in DOM order — the consumer's markup\n * first, then whatever the field resolved for the keys that markup left over.\n *\n * Only THIS field's own error block is read — its direct child, holding its\n * direct children — so a `<wr-form-field>` nested in another one's control slot\n * contributes nothing to the outer field's answer. Without that anchoring the\n * outer field reports the inner one's copy as its own, which is the same class\n * of break as reading a sibling field's messages.\n */\n async getErrors(): Promise<WrFormFieldError[]> {\n const elements = await this.locatorForAll(VISIBLE)();\n\n return Promise.all(\n elements.map(async element => ({\n key: await element.getAttribute('data-key'),\n text: await element.text(),\n // The consumer's copy is its own element; a resolved message is a plain\n // `<div>` the field stamped out. That tag name is the whole of what the\n // DOM knows about provenance.\n source:\n (await element.getProperty<string>('tagName')).toLowerCase() === 'wr-form-error'\n ? ('projected' as const)\n : ('resolved' as const),\n }))\n );\n }\n\n /** The visible messages as text, in DOM order. */\n async getErrorTexts(): Promise<string[]> {\n return (await this.getErrors()).map(error => error.text);\n }\n\n /**\n * The visible message for one validator key.\n *\n * Throws rather than answering `null` when that key has no visible message: the\n * two reasons are worth failing on and the message names both — the key is not\n * in error at all, or its copy resolved to nothing.\n */\n async getErrorText(key: string): Promise<string> {\n const errors = await this.getErrors();\n const match = errors.find(error => error.key === key);\n if (match) return match.text;\n\n const shown = errors.map(error => error.key ?? '(keyless)');\n const detail =\n shown.length > 0\n ? `Showing: ${shown.join(', ')}.`\n : 'The field is showing nothing — it is either valid, or the control is still untouched and pristine.';\n\n throw new Error(\n `WrFormFieldHarness.getErrorText(${JSON.stringify(key)}): no visible message for that key. ${detail}`\n );\n }\n\n /**\n * The keys of the projected messages the field is holding BACK — copy that is in\n * the DOM but hidden because its key is not among the failing ones.\n *\n * This is the gating contract seen from outside, and the reason a spec must not\n * query `.wr-form-error` itself: the suppressed sentences sit right next to the\n * live one.\n */\n async getSuppressedErrorKeys(): Promise<string[]> {\n const hidden = await this.locatorForAll(SUPPRESSED)();\n const keys = await Promise.all(hidden.map(element => element.getAttribute('data-key')));\n return keys.filter((key): key is string => key !== null);\n }\n\n /**\n * Whether the field is announcing an error it has no words for: invalid, with an\n * error block that renders nothing.\n *\n * Worth a named check because it is invisible in review — the field still goes\n * red, and `aria-describedby` still points at the block, so a screen reader is\n * sent to an empty element. It happens when no link in the copy chain answers\n * the key (`[autoErrors]=\"false\"` with no markup, or a key nothing has copy for\n * — Signal Forms reports `minLength`, while the chain only knows Angular's\n * lowercase `minlength`).\n */\n async hasEmptyErrorBlock(): Promise<boolean> {\n if (!(await this.isInvalid())) return false;\n return (await this.getErrors()).every(error => error.text.length === 0);\n }\n\n /** The id the `<label for>` points at, or `null` when there is no label. */\n async getLabelFor(): Promise<string | null> {\n const label = await this.labelElement();\n return label ? label.getAttribute('for') : null;\n }\n\n /** The `id` the projected control actually answers to, or `null` when it has none. */\n async getControlId(): Promise<string | null> {\n return (await this.control()).getAttribute('id');\n }\n\n /**\n * Whether the `<label for>` resolves to an element inside THIS field's control\n * slot — the only thing that makes the field nameable.\n *\n * Scoped to the slot on purpose: ids are document-global, so a check that went\n * through `getElementById` would also pass for a label pointing at a DIFFERENT\n * field's control, and for a control that has been moved out of the field\n * entirely. The failure this catches is quiet — a projected control that does\n * not adopt the field's `controlId` (a bare `<input>` with no `[wrInput]`, say)\n * leaves a `for` naming nothing, the field renders exactly as it always did, and\n * nothing is labelled.\n */\n async isLabelLinkedToControl(): Promise<boolean> {\n const id = await this.getLabelFor();\n if (!id) return false;\n return (await this.locatorForOptional(`${SLOT} [id=\"${id}\"]`)()) !== null;\n }\n\n /**\n * The ids the projected control names in `aria-describedby`, in order.\n *\n * The field points this at the error block and nothing else — the hint has no\n * id — so an empty list while a hint is showing is correct rather than a bug.\n * That is not an oversight to fix from a harness either: `[wrInput]` derives\n * `aria-invalid` from the same signal, so describing the hint would announce\n * every hinted field as invalid.\n */\n async getDescribedByIds(): Promise<string[]> {\n const value = await (await this.control()).getAttribute('aria-describedby');\n return value ? value.split(/\\s+/).filter(Boolean) : [];\n }\n\n /**\n * What a screen reader reads as the control's description: the text of the\n * elements `aria-describedby` names, joined by a space. `null` when the control\n * describes nothing.\n *\n * `''` is a real answer and the interesting one — the reference resolves to an\n * element with no text in it, which is {@link hasEmptyErrorBlock}. Ids naming\n * something outside this field are skipped (a consumer's own description is out\n * of a field harness's reach); an id naming nothing anywhere throws, because a\n * dangling reference is the bug this method exists to surface.\n *\n * Suppressed copy is excluded explicitly rather than left to CSS: a browser drops\n * `display: none` subtrees from the accessibility tree, but a unit test loads no\n * stylesheet at all, so the referenced block's raw `textContent` runs every\n * held-back sentence together with the live one and this would answer with copy\n * nobody can read.\n */\n async getAnnouncedDescription(): Promise<string | null> {\n const ids = await this.getDescribedByIds();\n if (ids.length === 0) return null;\n\n const parts: string[] = [];\n for (const id of ids) {\n const inField = await this.locatorForOptional(`[id=\"${id}\"]`)();\n if (inField) {\n parts.push(await inField.text({ exclude: '.wr-form-field__error--hidden' }));\n continue;\n }\n if ((await this.documentRootLocatorFactory().locatorForOptional(`[id=\"${id}\"]`)()) === null) {\n throw new Error(\n `WrFormFieldHarness.getAnnouncedDescription(): the control's aria-describedby names \"${id}\", which is not ` +\n 'an element anywhere in the document — a screen reader is pointed at nothing.'\n );\n }\n }\n\n return parts.join(' ');\n }\n\n /**\n * Whether the control announces itself invalid.\n *\n * `aria-invalid` on the projected control, which is what assistive technology\n * is told — the field's own red styling is {@link isInvalid}. Both are here\n * because they live on different elements and travel in opposite directions: the\n * field renders the message, the control reads back that there is one. A field\n * that paints red while the control says nothing is a real (and silent) break.\n */\n async isControlInvalid(): Promise<boolean> {\n return (await (await this.control()).getAttribute('aria-invalid')) === 'true';\n }\n\n /** Move focus to the projected control. */\n async focusControl(): Promise<void> {\n return (await this.control()).focus();\n }\n\n /**\n * Blur the projected control, which is usually how a spec gets any message to\n * appear at all: a bound control marks itself touched on blur, and the field\n * renders nothing while the control is untouched AND pristine.\n */\n async blurControl(): Promise<void> {\n return (await this.control()).blur();\n }\n\n /** The rendered `<label>`, or `null` when `label` is empty. */\n private async labelElement(): Promise<TestElement | null> {\n return this.locatorForOptional(LABEL)();\n }\n\n /**\n * The control this field wraps.\n *\n * The element the label points at, when that resolves inside the slot — which\n * covers a control nested inside a component (`<wr-input-number>`'s own\n * `<input>` adopts the field's id, so the label names it, not the component\n * element). Otherwise the first native control in the slot, so a field with no\n * label still answers.\n */\n private async control(): Promise<TestElement> {\n const id = await this.getLabelFor();\n if (id) {\n const labelled = await this.locatorForOptional(`${SLOT} [id=\"${id}\"]`)();\n if (labelled) return labelled;\n }\n\n const native = await this.locatorForOptional(`${SLOT} input, ${SLOT} select, ${SLOT} textarea`)();\n if (native) return native;\n\n throw new Error(\n 'WrFormFieldHarness: this field wraps no control. Nothing in `.wr-form-field__control` answers to the ' +\n \"label's `for`, and there is no <input> / <select> / <textarea> to fall back to.\"\n );\n }\n\n /**\n * The content loader every `HarnessLoader` method on this class runs through,\n * scoped to the control slot.\n *\n * So `field.getHarness(WrInputHarness)` finds the control this field wraps and\n * not a sibling field's — the loader is per-instance by construction, which is\n * what a document-wide query for the same selector would lose.\n */\n protected override async getRootHarnessLoader(): Promise<HarnessLoader> {\n return this.locatorFactory.harnessLoaderFor(SLOT);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ContentContainerComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrFormItemHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-form-item>` — the layout-only field row.\n *\n * Deliberately a much smaller surface than {@link WrFormFieldHarness}, because\n * the component is a much smaller thing: it discovers no control, generates no\n * id, wires no `for` and no `aria-describedby`, and reads no validity. It paints\n * the error state it is TOLD to paint, via `[hasError]`, and everything else in\n * it belongs to the consumer — including the `<label>`, which is named here\n * rather than linked (nothing wires it; the consumer's own `for` is the only\n * thing that can).\n *\n * The one behavioural trap it is worth a harness for: a `<wr-form-error>` inside\n * a form-item renders UNCONDITIONALLY. `key` has no effect at all — the gating\n * lives in the surrounding `<wr-form-field>`, and with no field to ask, every\n * message decides it is the one to show. Markup moved from a field into an item\n * therefore starts printing every sentence at once, and {@link getErrorTexts}\n * is what says so.\n *\n * @example\n * ```ts\n * const item = await loader.getHarness(WrFormItemHarness.with({ label: 'Email' }));\n *\n * expect(await item.isInvalid()).toBe(true);\n * expect(await item.getErrorTexts()).toEqual(['Invalid email']);\n * await (await item.getHarness(WrInputHarness)).setValue('ada@example.test');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrFormItemHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-form-item';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrFormItemHarnessFilters = {}): HarnessPredicate<WrFormItemHarness> {\n return new HarnessPredicate(WrFormItemHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('invalid', options.invalid, async (harness, invalid) => (await harness.isInvalid()) === invalid)\n .addOption('errorText', options.errorText, async (harness, text) => {\n for (const shown of await harness.getErrorTexts()) {\n if (await HarnessPredicate.stringMatches(shown, text)) return true;\n }\n return false;\n });\n }\n\n /**\n * The text of the item's own `<label>` child, or `null` when it has none.\n *\n * Direct child only — that is what the item styles (`> label` is what turns red\n * in the error state), so a label nested deeper is the consumer's own business\n * and answering with it would describe something the item never touches.\n */\n async getLabel(): Promise<string | null> {\n const label = await this.locatorForOptional(':scope > label')();\n return label ? label.text() : null;\n }\n\n /**\n * Whether the item is painting the error state.\n *\n * The `wr-form-item--error` modifier, which is driven entirely by the\n * consumer's `[hasError]` — the item runs no validity check of its own, so this\n * answers what it was told, not what the control thinks.\n */\n async isInvalid(): Promise<boolean> {\n return (await this.host()).hasClass('wr-form-item--error');\n }\n\n /**\n * Every `<wr-form-error>` in the item, as text, in DOM order.\n *\n * All of them: nothing is gated here, `key` or no `key`. Nothing is ever\n * hidden either, so there is no suppressed-message counterpart to this — see\n * the class docs.\n */\n async getErrorTexts(): Promise<string[]> {\n const errors = await this.locatorForAll('wr-form-error')();\n return Promise.all(errors.map(error => error.text()));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
2
  import * as i0 from '@angular/core';
3
- import { input, computed, ViewEncapsulation, Component, InjectionToken, makeEnvironmentProviders, inject, contentChild, signal, effect, DestroyRef, contentChildren, forwardRef } from '@angular/core';
3
+ import { input, computed, ViewEncapsulation, Component, InjectionToken, makeEnvironmentProviders, inject, signal, contentChild, effect, DestroyRef, contentChildren, forwardRef } from '@angular/core';
4
4
  import { NgControl } from '@angular/forms';
5
5
  import { EMPTY } from 'rxjs';
6
6
  import { WrI18n } from 'ngwr/i18n';
@@ -42,10 +42,10 @@ class WrFormItem {
42
42
  return parts.join(' ');
43
43
  }, /* @ts-ignore */
44
44
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
45
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
46
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: WrFormItem, isStandalone: true, selector: "wr-form-item", inputs: { hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
45
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
46
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrFormItem, isStandalone: true, selector: "wr-form-item", inputs: { hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
47
47
  }
48
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormItem, decorators: [{
48
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormItem, decorators: [{
49
49
  type: Component,
50
50
  args: [{
51
51
  selector: 'wr-form-item',
@@ -197,10 +197,10 @@ class WrFormError {
197
197
  return this.field.errorKeys().includes(key);
198
198
  }, /* @ts-ignore */
199
199
  ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
200
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormError, deps: [], target: i0.ɵɵFactoryTarget.Component });
201
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: WrFormError, isStandalone: true, selector: "wr-form-error", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "alert" }, properties: { "attr.data-key": "key() ?? null", "class.wr-form-field__error--hidden": "!visible()" }, classAttribute: "wr-form-error wr-form-field__error" }, ngImport: i0, template: `<ng-content />`, isInline: true, encapsulation: i0.ViewEncapsulation.None });
200
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormError, deps: [], target: i0.ɵɵFactoryTarget.Component });
201
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrFormError, isStandalone: true, selector: "wr-form-error", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "alert" }, properties: { "attr.data-key": "key() ?? null", "class.wr-form-field__error--hidden": "!visible()" }, classAttribute: "wr-form-error wr-form-field__error" }, ngImport: i0, template: `<ng-content />`, isInline: true, encapsulation: i0.ViewEncapsulation.None });
202
202
  }
203
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormError, decorators: [{
203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormError, decorators: [{
204
204
  type: Component,
205
205
  args: [{
206
206
  selector: 'wr-form-error',
@@ -253,6 +253,22 @@ class WrFormField {
253
253
  */
254
254
  controlId = input(`wr-form-field-${++uid}`, /* @ts-ignore */
255
255
  ...(ngDevMode ? [{ debugName: "controlId" }] : /* istanbul ignore next */ []));
256
+ // What a projected control reported it actually kept. `null` until one does — most
257
+ // controls simply adopt `controlId`, and only a control with its own `id` overrides
258
+ // it from the other direction.
259
+ adoptedId = signal(null, /* @ts-ignore */
260
+ ...(ngDevMode ? [{ debugName: "adoptedId" }] : /* istanbul ignore next */ []));
261
+ /**
262
+ * Id the `<label for>` renders. A control that came with its own `id` keeps it, so
263
+ * the label follows that instead of the generated one — otherwise `for` points at
264
+ * an element that is not in the document and the field is unlabelled.
265
+ */
266
+ labelFor = computed(() => this.adoptedId() ?? this.controlId(), /* @ts-ignore */
267
+ ...(ngDevMode ? [{ debugName: "labelFor" }] : /* istanbul ignore next */ []));
268
+ /** @internal `WrFormFieldContext` — see the token's docs. */
269
+ adoptControlId(id) {
270
+ this.adoptedId.set(id);
271
+ }
256
272
  /**
257
273
  * Projected `NgControl` — used to read touched / dirty / errors.
258
274
  *
@@ -359,16 +375,16 @@ class WrFormField {
359
375
  return parts.join(' ');
360
376
  }, /* @ts-ignore */
361
377
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
362
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
363
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: WrFormField, isStandalone: true, selector: "wr-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, autoErrors: { classPropertyName: "autoErrors", publicName: "autoErrors", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [
378
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
379
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrFormField, isStandalone: true, selector: "wr-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, autoErrors: { classPropertyName: "autoErrors", publicName: "autoErrors", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [
364
380
  {
365
381
  provide: WR_FORM_FIELD,
366
382
  // eslint-disable-next-line @angular-eslint/no-forward-ref
367
383
  useExisting: forwardRef(() => WrFormField),
368
384
  },
369
- ], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "projectedErrors", predicate: WrFormError, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"controlId()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(optional)</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n", encapsulation: i0.ViewEncapsulation.None });
385
+ ], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "projectedErrors", predicate: WrFormError, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(optional)</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n", encapsulation: i0.ViewEncapsulation.None });
370
386
  }
371
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrFormField, decorators: [{
387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrFormField, decorators: [{
372
388
  type: Component,
373
389
  args: [{ selector: 'wr-form-field', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, providers: [
374
390
  {
@@ -376,7 +392,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
376
392
  // eslint-disable-next-line @angular-eslint/no-forward-ref
377
393
  useExisting: forwardRef(() => WrFormField),
378
394
  },
379
- ], template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"controlId()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(optional)</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n" }]
395
+ ], template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(optional)</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n" }]
380
396
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }], autoErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoErrors", required: false }] }], projectedErrors: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrFormError), { isSignal: true }] }] } });
381
397
 
382
398
  /**