@wix/patterns-fields 1.94.0 → 1.96.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 (250) hide show
  1. package/dist/cjs/components/address/input/address-input.js +127 -36
  2. package/dist/cjs/components/address/input/address-input.js.map +1 -1
  3. package/dist/cjs/components/address/input/address-input.st.css.js +3 -3
  4. package/dist/cjs/components/address/input/address-input.st.css.js.map +1 -1
  5. package/dist/cjs/components/audio/actions/actions-menu/actions-menu.st.css.js +3 -3
  6. package/dist/cjs/components/audio/actions/actions-menu/actions-menu.st.css.js.map +1 -1
  7. package/dist/cjs/components/audio/audio-field/controlled-audio.uni.driver.js +3 -2
  8. package/dist/cjs/components/audio/audio-field/controlled-audio.uni.driver.js.map +1 -1
  9. package/dist/cjs/components/audio/audio-field/form-audio-field.js +55 -48
  10. package/dist/cjs/components/audio/audio-field/form-audio-field.js.map +1 -1
  11. package/dist/cjs/components/audio/audio-field/form-audio-field.st.css.js +4 -4
  12. package/dist/cjs/components/audio/audio-field/form-audio-field.st.css.js.map +1 -1
  13. package/dist/cjs/components/audio/audio-player/audio-player.st.css.js +2 -2
  14. package/dist/cjs/components/audio/audio-player/audio-player.st.css.js.map +1 -1
  15. package/dist/cjs/components/color-view/cell-color-view.st.css.js +3 -3
  16. package/dist/cjs/components/color-view/cell-color-view.st.css.js.map +1 -1
  17. package/dist/cjs/components/color-view/color-view.st.css.js +4 -4
  18. package/dist/cjs/components/color-view/color-view.st.css.js.map +1 -1
  19. package/dist/cjs/components/delete-dialog/delete-dialog.st.css.js +3 -3
  20. package/dist/cjs/components/delete-dialog/delete-dialog.st.css.js.map +1 -1
  21. package/dist/cjs/components/document/form-document-field.st.css.js +7 -7
  22. package/dist/cjs/components/document/form-document-field.st.css.js.map +1 -1
  23. package/dist/cjs/components/exclamation/exclamation.st.css.js +5 -5
  24. package/dist/cjs/components/exclamation/exclamation.st.css.js.map +1 -1
  25. package/dist/cjs/components/field-validation-settings/field-validation-settings-input.js +85 -49
  26. package/dist/cjs/components/field-validation-settings/field-validation-settings-input.js.map +1 -1
  27. package/dist/cjs/components/field-validation-settings/field-validation-settings.uni.driver.js +2 -1
  28. package/dist/cjs/components/field-validation-settings/field-validation-settings.uni.driver.js.map +1 -1
  29. package/dist/cjs/components/highlighted-text/highlighted-text.st.css.js +5 -5
  30. package/dist/cjs/components/highlighted-text/highlighted-text.st.css.js.map +1 -1
  31. package/dist/cjs/components/image/thumbnail-replace-overlay.st.css.js +3 -3
  32. package/dist/cjs/components/image/thumbnail-replace-overlay.st.css.js.map +1 -1
  33. package/dist/cjs/components/media-control/cell-media-edit-shell.st.css.js +2 -2
  34. package/dist/cjs/components/media-control/cell-media-edit-shell.st.css.js.map +1 -1
  35. package/dist/cjs/components/media-control/paste-url-button.st.css.js +4 -4
  36. package/dist/cjs/components/media-control/paste-url-button.st.css.js.map +1 -1
  37. package/dist/cjs/components/media-gallery/form-media-gallery-field.st.css.js +10 -10
  38. package/dist/cjs/components/media-gallery/form-media-gallery-field.st.css.js.map +1 -1
  39. package/dist/cjs/components/media-image/media-image.st.css.js +4 -4
  40. package/dist/cjs/components/media-image/media-image.st.css.js.map +1 -1
  41. package/dist/cjs/components/media-image/overlays/overlay.st.css.js +3 -3
  42. package/dist/cjs/components/media-image/overlays/overlay.st.css.js.map +1 -1
  43. package/dist/cjs/components/media-tag/web-media-tag/media-tag.st.css.js +2 -2
  44. package/dist/cjs/components/media-tag/web-media-tag/media-tag.st.css.js.map +1 -1
  45. package/dist/cjs/components/multi-document/cell-multi-document-edit.st.css.js +3 -3
  46. package/dist/cjs/components/multi-document/cell-multi-document-edit.st.css.js.map +1 -1
  47. package/dist/cjs/components/multi-document/multi-document-input/form-multi-document.st.css.js +5 -5
  48. package/dist/cjs/components/multi-document/multi-document-input/form-multi-document.st.css.js.map +1 -1
  49. package/dist/cjs/components/reference/cell-multi-reference-input.st.css.js +2 -2
  50. package/dist/cjs/components/reference/cell-multi-reference-input.st.css.js.map +1 -1
  51. package/dist/cjs/components/reference/cell-reference-input.st.css.js +3 -3
  52. package/dist/cjs/components/reference/cell-reference-input.st.css.js.map +1 -1
  53. package/dist/cjs/components/rich-content/rich-content-input/default-value-input/rich-content-default-value-input.st.css.js +4 -4
  54. package/dist/cjs/components/rich-content/rich-content-input/default-value-input/rich-content-default-value-input.st.css.js.map +1 -1
  55. package/dist/cjs/components/rich-content/rich-content-input/form-input/rich-content-form-input.st.css.js +3 -3
  56. package/dist/cjs/components/rich-content/rich-content-input/form-input/rich-content-form-input.st.css.js.map +1 -1
  57. package/dist/cjs/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.js +2 -2
  58. package/dist/cjs/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.js.map +1 -1
  59. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.js +3 -3
  60. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.js.map +1 -1
  61. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.js +3 -3
  62. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.js.map +1 -1
  63. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/toggle-fullscreen-button/toggle-fullscreen-button.st.css.js +2 -2
  64. package/dist/cjs/components/rich-content/rich-content-input/rich-content-common/toggle-fullscreen-button/toggle-fullscreen-button.st.css.js.map +1 -1
  65. package/dist/cjs/components/rich-content/rich-content-input/rich-content-editor/rich-content-editor.st.css.js +7 -7
  66. package/dist/cjs/components/rich-content/rich-content-input/rich-content-editor/rich-content-editor.st.css.js.map +1 -1
  67. package/dist/cjs/components/rich-content/rich-content-input/rich-content-editor/rich-content-toolbar.st.css.js +5 -5
  68. package/dist/cjs/components/rich-content/rich-content-input/rich-content-editor/rich-content-toolbar.st.css.js.map +1 -1
  69. package/dist/cjs/components/rich-content/rich-content-input/rich-content-viewer/rich-content-viewer.st.css.js +4 -4
  70. package/dist/cjs/components/rich-content/rich-content-input/rich-content-viewer/rich-content-viewer.st.css.js.map +1 -1
  71. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/rich-editor.st.css.js +18 -18
  72. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/rich-editor.st.css.js.map +1 -1
  73. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/rich-text-editor.st.css.js +4 -4
  74. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/rich-text-editor.st.css.js.map +1 -1
  75. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/toolbar/toolbar.st.css.js +18 -18
  76. package/dist/cjs/components/rich-text/rich-text-input/rich-text-editor/toolbar/toolbar.st.css.js.map +1 -1
  77. package/dist/cjs/components/text/text-view.st.css.js +3 -3
  78. package/dist/cjs/components/text/text-view.st.css.js.map +1 -1
  79. package/dist/cjs/components/url/form-url.js +22 -9
  80. package/dist/cjs/components/url/form-url.js.map +1 -1
  81. package/dist/cjs/components/video/form-video-field.js +35 -28
  82. package/dist/cjs/components/video/form-video-field.js.map +1 -1
  83. package/dist/cjs/services/atlas/types.js.map +1 -1
  84. package/dist/cjs/services/atlas/wix-atlas-service.js +16 -1
  85. package/dist/cjs/services/atlas/wix-atlas-service.js.map +1 -1
  86. package/dist/cjs/styles.global.css +1 -1
  87. package/dist/cjs/types/validation.js.map +1 -1
  88. package/dist/esm/components/address/input/address-input.js +139 -38
  89. package/dist/esm/components/address/input/address-input.js.map +1 -1
  90. package/dist/esm/components/address/input/address-input.st.css.js +2 -2
  91. package/dist/esm/components/audio/actions/actions-menu/actions-menu.st.css.js +2 -2
  92. package/dist/esm/components/audio/audio-field/controlled-audio.uni.driver.js +3 -2
  93. package/dist/esm/components/audio/audio-field/controlled-audio.uni.driver.js.map +1 -1
  94. package/dist/esm/components/audio/audio-field/form-audio-field.js +9 -13
  95. package/dist/esm/components/audio/audio-field/form-audio-field.js.map +1 -1
  96. package/dist/esm/components/audio/audio-field/form-audio-field.st.css.js +2 -2
  97. package/dist/esm/components/audio/audio-field/form-audio-field.st.css.js.map +1 -1
  98. package/dist/esm/components/audio/audio-player/audio-player.st.css.js +2 -2
  99. package/dist/esm/components/audio/audio-player/audio-player.st.css.js.map +1 -1
  100. package/dist/esm/components/color-view/cell-color-view.st.css.js +2 -2
  101. package/dist/esm/components/color-view/color-view.st.css.js +2 -2
  102. package/dist/esm/components/color-view/color-view.st.css.js.map +1 -1
  103. package/dist/esm/components/delete-dialog/delete-dialog.st.css.js +2 -2
  104. package/dist/esm/components/document/form-document-field.st.css.js +2 -2
  105. package/dist/esm/components/exclamation/exclamation.st.css.js +2 -2
  106. package/dist/esm/components/field-validation-settings/field-validation-settings-input.js +45 -10
  107. package/dist/esm/components/field-validation-settings/field-validation-settings-input.js.map +1 -1
  108. package/dist/esm/components/field-validation-settings/field-validation-settings.uni.driver.js +2 -1
  109. package/dist/esm/components/field-validation-settings/field-validation-settings.uni.driver.js.map +1 -1
  110. package/dist/esm/components/highlighted-text/highlighted-text.st.css.js +3 -3
  111. package/dist/esm/components/highlighted-text/highlighted-text.st.css.js.map +1 -1
  112. package/dist/esm/components/image/thumbnail-replace-overlay.st.css.js +2 -2
  113. package/dist/esm/components/media-control/cell-media-edit-shell.st.css.js +2 -2
  114. package/dist/esm/components/media-control/paste-url-button.st.css.js +2 -2
  115. package/dist/esm/components/media-gallery/form-media-gallery-field.st.css.js +2 -2
  116. package/dist/esm/components/media-image/media-image.st.css.js +3 -3
  117. package/dist/esm/components/media-image/overlays/overlay.st.css.js +2 -2
  118. package/dist/esm/components/media-tag/web-media-tag/media-tag.st.css.js +2 -2
  119. package/dist/esm/components/multi-document/cell-multi-document-edit.st.css.js +2 -2
  120. package/dist/esm/components/multi-document/cell-multi-document-edit.st.css.js.map +1 -1
  121. package/dist/esm/components/multi-document/multi-document-input/form-multi-document.st.css.js +2 -2
  122. package/dist/esm/components/reference/cell-multi-reference-input.st.css.js +2 -2
  123. package/dist/esm/components/reference/cell-multi-reference-input.st.css.js.map +1 -1
  124. package/dist/esm/components/reference/cell-reference-input.st.css.js +2 -2
  125. package/dist/esm/components/rich-content/rich-content-input/default-value-input/rich-content-default-value-input.st.css.js +2 -2
  126. package/dist/esm/components/rich-content/rich-content-input/form-input/rich-content-form-input.st.css.js +2 -2
  127. package/dist/esm/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.js +2 -2
  128. package/dist/esm/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.js.map +1 -1
  129. package/dist/esm/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.js +2 -2
  130. package/dist/esm/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.js.map +1 -1
  131. package/dist/esm/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.js +2 -2
  132. package/dist/esm/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.js.map +1 -1
  133. package/dist/esm/components/rich-content/rich-content-input/rich-content-common/toggle-fullscreen-button/toggle-fullscreen-button.st.css.js +2 -2
  134. package/dist/esm/components/rich-content/rich-content-input/rich-content-editor/rich-content-editor.st.css.js +2 -2
  135. package/dist/esm/components/rich-content/rich-content-input/rich-content-editor/rich-content-toolbar.st.css.js +2 -2
  136. package/dist/esm/components/rich-content/rich-content-input/rich-content-viewer/rich-content-viewer.st.css.js +2 -2
  137. package/dist/esm/components/rich-text/rich-text-input/rich-text-editor/rich-editor.st.css.js +2 -2
  138. package/dist/esm/components/rich-text/rich-text-input/rich-text-editor/rich-text-editor.st.css.js +2 -2
  139. package/dist/esm/components/rich-text/rich-text-input/rich-text-editor/toolbar/toolbar.st.css.js +2 -2
  140. package/dist/esm/components/text/text-view.st.css.js +2 -2
  141. package/dist/esm/components/url/form-url.js +14 -1
  142. package/dist/esm/components/url/form-url.js.map +1 -1
  143. package/dist/esm/components/video/form-video-field.js +8 -1
  144. package/dist/esm/components/video/form-video-field.js.map +1 -1
  145. package/dist/esm/services/atlas/wix-atlas-service.js +22 -2
  146. package/dist/esm/services/atlas/wix-atlas-service.js.map +1 -1
  147. package/dist/esm/styles.global.css +1 -1
  148. package/dist/types/components/address/input/address-input.d.ts.map +1 -1
  149. package/dist/types/components/audio/audio-field/controlled-audio.uni.driver.d.ts +1 -0
  150. package/dist/types/components/audio/audio-field/controlled-audio.uni.driver.d.ts.map +1 -1
  151. package/dist/types/components/audio/audio-field/form-audio-field.d.ts.map +1 -1
  152. package/dist/types/components/audio/audio-field/form-audio-field.st.css.d.ts.map +1 -1
  153. package/dist/types/components/audio/audio-player/audio-player.st.css.d.ts.map +1 -1
  154. package/dist/types/components/color-view/color-view.st.css.d.ts.map +1 -1
  155. package/dist/types/components/controlled-input.uni.driver.d.ts +1 -0
  156. package/dist/types/components/controlled-input.uni.driver.d.ts.map +1 -1
  157. package/dist/types/components/field-validation-settings/field-validation-settings-input.d.ts +4 -1
  158. package/dist/types/components/field-validation-settings/field-validation-settings-input.d.ts.map +1 -1
  159. package/dist/types/components/field-validation-settings/field-validation-settings.uni.driver.d.ts +7 -0
  160. package/dist/types/components/field-validation-settings/field-validation-settings.uni.driver.d.ts.map +1 -1
  161. package/dist/types/components/highlighted-text/highlighted-text.st.css.d.ts.map +1 -1
  162. package/dist/types/components/multi-document/cell-multi-document-edit.st.css.d.ts.map +1 -1
  163. package/dist/types/components/reference/cell-multi-reference-input.st.css.d.ts.map +1 -1
  164. package/dist/types/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.d.ts.map +1 -1
  165. package/dist/types/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.d.ts.map +1 -1
  166. package/dist/types/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.d.ts.map +1 -1
  167. package/dist/types/components/url/form-url.d.ts.map +1 -1
  168. package/dist/types/components/video/form-video-field.d.ts.map +1 -1
  169. package/dist/types/services/atlas/types.d.ts +5 -1
  170. package/dist/types/services/atlas/types.d.ts.map +1 -1
  171. package/dist/types/services/atlas/wix-atlas-service.d.ts +6 -1
  172. package/dist/types/services/atlas/wix-atlas-service.d.ts.map +1 -1
  173. package/dist/types/types/validation.d.ts +6 -1
  174. package/dist/types/types/validation.d.ts.map +1 -1
  175. package/package.json +4 -4
  176. package/src/__tests__/components/address/address-input.driver.tsx +17 -1
  177. package/src/__tests__/components/address/address-input.spec.tsx +207 -1
  178. package/src/__tests__/components/field-validation-settings/field-validation-settings-input.spec.tsx +87 -0
  179. package/src/__tests__/components/url/form-url.spec.tsx +63 -0
  180. package/src/__tests__/components/video/form-video.spec.tsx +17 -0
  181. package/src/__tests__/exports/controlled-inputs.spec.tsx +46 -0
  182. package/src/components/address/input/address-input.st.css.ts +2 -2
  183. package/src/components/address/input/address-input.tsx +163 -50
  184. package/src/components/audio/actions/actions-menu/actions-menu.st.css.ts +2 -2
  185. package/src/components/audio/audio-field/controlled-audio.uni.driver.ts +3 -2
  186. package/src/components/audio/audio-field/form-audio-field.st.css.ts +2 -2
  187. package/src/components/audio/audio-field/form-audio-field.tsx +59 -53
  188. package/src/components/audio/audio-player/audio-player.st.css.ts +2 -2
  189. package/src/components/color-view/cell-color-view.st.css.ts +2 -2
  190. package/src/components/color-view/color-view.st.css.ts +2 -2
  191. package/src/components/delete-dialog/delete-dialog.st.css.ts +2 -2
  192. package/src/components/document/form-document-field.st.css.ts +2 -2
  193. package/src/components/exclamation/exclamation.st.css.ts +2 -2
  194. package/src/components/field-validation-settings/field-validation-settings-input.tsx +75 -11
  195. package/src/components/field-validation-settings/field-validation-settings.uni.driver.ts +3 -1
  196. package/src/components/highlighted-text/highlighted-text.st.css.ts +3 -3
  197. package/src/components/image/thumbnail-replace-overlay.st.css.ts +2 -2
  198. package/src/components/media-control/cell-media-edit-shell.st.css.ts +2 -2
  199. package/src/components/media-control/paste-url-button.st.css.ts +2 -2
  200. package/src/components/media-gallery/form-media-gallery-field.st.css.ts +2 -2
  201. package/src/components/media-image/media-image.st.css.ts +3 -3
  202. package/src/components/media-image/overlays/overlay.st.css.ts +2 -2
  203. package/src/components/media-tag/web-media-tag/media-tag.st.css.ts +2 -2
  204. package/src/components/multi-document/cell-multi-document-edit.st.css.ts +2 -2
  205. package/src/components/multi-document/multi-document-input/form-multi-document.st.css.ts +2 -2
  206. package/src/components/reference/cell-multi-reference-input.st.css.ts +2 -2
  207. package/src/components/reference/cell-reference-input.st.css.ts +2 -2
  208. package/src/components/rich-content/rich-content-input/default-value-input/rich-content-default-value-input.st.css.ts +2 -2
  209. package/src/components/rich-content/rich-content-input/form-input/rich-content-form-input.st.css.ts +2 -2
  210. package/src/components/rich-content/rich-content-input/form-read-only-input/rich-content-form-read-only-input.st.css.ts +2 -2
  211. package/src/components/rich-content/rich-content-input/rich-content-common/fullscreen-modal/fullscreen-modal.st.css.ts +2 -2
  212. package/src/components/rich-content/rich-content-input/rich-content-common/publish-loader/publish-loader.st.css.ts +2 -2
  213. package/src/components/rich-content/rich-content-input/rich-content-common/toggle-fullscreen-button/toggle-fullscreen-button.st.css.ts +2 -2
  214. package/src/components/rich-content/rich-content-input/rich-content-editor/rich-content-editor.st.css.ts +2 -2
  215. package/src/components/rich-content/rich-content-input/rich-content-editor/rich-content-toolbar.st.css.ts +2 -2
  216. package/src/components/rich-content/rich-content-input/rich-content-viewer/rich-content-viewer.st.css.ts +2 -2
  217. package/src/components/rich-text/rich-text-input/rich-text-editor/rich-editor.st.css.ts +2 -2
  218. package/src/components/rich-text/rich-text-input/rich-text-editor/rich-text-editor.st.css.ts +2 -2
  219. package/src/components/rich-text/rich-text-input/rich-text-editor/toolbar/toolbar.st.css.ts +2 -2
  220. package/src/components/text/text-view.st.css.ts +2 -2
  221. package/src/components/url/form-url.tsx +16 -1
  222. package/src/components/video/form-video-field.tsx +6 -0
  223. package/src/services/atlas/types.ts +6 -1
  224. package/src/services/atlas/wix-atlas-service.ts +27 -3
  225. package/src/styles.global.css +1 -1
  226. package/src/types/validation.ts +6 -1
  227. package/dist/cjs/components/media-loader/index.js +0 -10
  228. package/dist/cjs/components/media-loader/index.js.map +0 -1
  229. package/dist/cjs/components/media-loader/media-loader.js +0 -37
  230. package/dist/cjs/components/media-loader/media-loader.js.map +0 -1
  231. package/dist/cjs/components/media-loader/media-loader.st.css +0 -20
  232. package/dist/cjs/components/media-loader/media-loader.st.css.js +0 -29
  233. package/dist/cjs/components/media-loader/media-loader.st.css.js.map +0 -1
  234. package/dist/esm/components/media-loader/index.js +0 -2
  235. package/dist/esm/components/media-loader/index.js.map +0 -1
  236. package/dist/esm/components/media-loader/media-loader.js +0 -8
  237. package/dist/esm/components/media-loader/media-loader.js.map +0 -1
  238. package/dist/esm/components/media-loader/media-loader.st.css +0 -20
  239. package/dist/esm/components/media-loader/media-loader.st.css.js +0 -15
  240. package/dist/esm/components/media-loader/media-loader.st.css.js.map +0 -1
  241. package/dist/types/components/media-loader/index.d.ts +0 -2
  242. package/dist/types/components/media-loader/index.d.ts.map +0 -1
  243. package/dist/types/components/media-loader/media-loader.d.ts +0 -7
  244. package/dist/types/components/media-loader/media-loader.d.ts.map +0 -1
  245. package/dist/types/components/media-loader/media-loader.st.css.d.ts +0 -20
  246. package/dist/types/components/media-loader/media-loader.st.css.d.ts.map +0 -1
  247. package/src/components/media-loader/index.ts +0 -1
  248. package/src/components/media-loader/media-loader.st.css +0 -20
  249. package/src/components/media-loader/media-loader.st.css.ts +0 -25
  250. package/src/components/media-loader/media-loader.tsx +0 -30
@@ -28,6 +28,8 @@ export interface CharactersLimitFieldValidationRuleConfig {
28
28
  separator: string;
29
29
  outOfBoundsError(min: number, max: number): string;
30
30
  maxBelowMinError: string;
31
+ /** Shown under this rule while another rule blocks it. */
32
+ exclusiveRulesExplanation?: string;
31
33
  }
32
34
  export interface SpecificValuesFieldValidationRuleConfig {
33
35
  type: 'specificValues';
@@ -37,7 +39,8 @@ export interface SpecificValuesFieldValidationRuleConfig {
37
39
  duplicateError: string;
38
40
  addButton: string;
39
41
  removeButton: string;
40
- exclusiveRulesExplanation: string;
42
+ /** Shown under this rule while another rule blocks it. */
43
+ exclusiveRulesExplanation?: string;
41
44
  }
42
45
  export interface PatternFieldValidationRuleConfig {
43
46
  type: 'pattern';
@@ -48,6 +51,8 @@ export interface PatternFieldValidationRuleConfig {
48
51
  /** Shown for a pattern that compiles but is refused because matching it
49
52
  * could hang the page. Falls back to `invalidError`. */
50
53
  unsafeError?: string;
54
+ /** Shown under this rule while another rule blocks it. */
55
+ exclusiveRulesExplanation?: string;
51
56
  }
52
57
  export type FieldValidationRuleConfig = RequiredFieldValidationRuleConfig | CharactersLimitFieldValidationRuleConfig | SpecificValuesFieldValidationRuleConfig | PatternFieldValidationRuleConfig;
53
58
  export type FieldValidationRule = FieldValidationRuleConfig['type'];
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/types/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/types/validation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/patterns-fields",
3
- "version": "1.94.0",
3
+ "version": "1.96.0",
4
4
  "license": "UNLICENSED",
5
5
  "author": {
6
6
  "name": "Kobi",
@@ -124,7 +124,7 @@
124
124
  "@wix/atlas": "^1.0.35",
125
125
  "@wix/data": "^1.0.323",
126
126
  "@wix/media": "^1.0.231",
127
- "@wix/patterns-fields-utils": "1.75.0",
127
+ "@wix/patterns-fields-utils": "1.77.0",
128
128
  "@wix/rich-content": "1.0.37",
129
129
  "@wix/sdk": "^1.17.2",
130
130
  "@wix/wix-ui-icons-common": "^3.189.0",
@@ -152,7 +152,7 @@
152
152
  "@types/lodash": "^4.17.20",
153
153
  "@types/react": "^16.14.68",
154
154
  "@wix/babel-cli": "^1.11.0",
155
- "@wix/bex-core": "2.372.0",
155
+ "@wix/bex-core": "2.374.0",
156
156
  "@wix/design-system": "^1.319.0",
157
157
  "@wix/eslint-config-yoshi": "^6.74.0",
158
158
  "@wix/jest-yoshi-preset": "^6.74.0",
@@ -212,5 +212,5 @@
212
212
  "optional": true
213
213
  }
214
214
  },
215
- "falconPackageHash": "c36b9b42fe9f94f1f1ffc680446db7f8570c58fdc394dc0e4a5e37b7"
215
+ "falconPackageHash": "5cd0bb88af543328ef37718c741e23a8661d02f7f12673a9410dde77"
216
216
  }
@@ -8,6 +8,7 @@ import { AddressInputUniDriver } from '../../../components/address/input/address
8
8
  import { uniTestkitFactoryCreator } from '@wix/wix-ui-test-utils/testing-library';
9
9
  import { flush } from '@wix/bex-core/testkit';
10
10
  import { PatternsFieldsTestProvider } from '../../../testkit/patterns-fields-test-provider';
11
+ import { InputUniDriver } from '@wix/design-system/dist/testkit/unidriver';
11
12
 
12
13
  const SEARCH_DEBOUNCE_MS = 250;
13
14
 
@@ -36,10 +37,19 @@ export class AddressInputDriver {
36
37
 
37
38
  waitForDebounce() {
38
39
  return act(async () => {
40
+ // Legacy timers do not advance the clock lodash's debounce reads.
41
+ jest.spyOn(Date, 'now').mockReturnValue(Date.now() + SEARCH_DEBOUNCE_MS);
39
42
  jest.advanceTimersByTime(SEARCH_DEBOUNCE_MS);
40
43
  });
41
44
  }
42
45
 
46
+ pressEscape() {
47
+ return uniTestkitFactoryCreator(InputUniDriver)({
48
+ wrapper: this.renderResult.container,
49
+ dataHook: 'address-input',
50
+ }).keyDown({ key: 'Escape' });
51
+ }
52
+
43
53
  render(props: AddressInputProps) {
44
54
  this._renderResult = render(
45
55
  <AddressInput {...props} atlasService={this._mockService} />,
@@ -47,7 +57,13 @@ export class AddressInputDriver {
47
57
  );
48
58
  }
49
59
 
60
+ unmount() {
61
+ this.renderResult.unmount();
62
+ }
63
+
50
64
  async flush(iterations?: number) {
51
- return flush(iterations);
65
+ await act(async () => {
66
+ await flush(iterations);
67
+ });
52
68
  }
53
69
  }
@@ -6,7 +6,22 @@ import {
6
6
  createAtlasServiceMock,
7
7
  } from '../../../services/atlas/atlas-service-mock';
8
8
 
9
+ const mockPredict = jest.fn();
10
+ const mockGetPlace = jest.fn();
11
+
12
+ jest.mock('@wix/atlas', () => ({
13
+ autocomplete: { predict: (...args: unknown[]) => mockPredict(...args) },
14
+ places: { getPlace: (...args: unknown[]) => mockGetPlace(...args) },
15
+ location: { search: jest.fn() },
16
+ }));
17
+
9
18
  describe('AddressInput', () => {
19
+ beforeEach(() => {
20
+ jest.useFakeTimers('legacy');
21
+ jest.clearAllMocks();
22
+ });
23
+ afterEach(() => jest.restoreAllMocks());
24
+
10
25
  const mockAddress: Address = {
11
26
  formatted: 'Halimon 25, Tel Mond, IL',
12
27
  city: 'Tel Mond',
@@ -16,12 +31,16 @@ describe('AddressInput', () => {
16
31
  const setup = async ({
17
32
  mockAtlasService,
18
33
  initialValue,
34
+ onChange,
35
+ onSelectAddress,
19
36
  }: {
20
37
  mockAtlasService?: AtlasService;
21
38
  initialValue?: Address;
39
+ onChange?: (value?: Address) => void;
40
+ onSelectAddress?: () => void;
22
41
  } = {}) => {
23
42
  const driver = new AddressInputDriver({ service: mockAtlasService });
24
- driver.render({ value: initialValue });
43
+ driver.render({ value: initialValue, onChange, onSelectAddress });
25
44
 
26
45
  await driver.flush();
27
46
  return { driver };
@@ -72,4 +91,191 @@ describe('AddressInput', () => {
72
91
  expect(await driver.addressInput.getInputValue()).toBe('Test address');
73
92
  expect(await driver.addressInput.getAmountOfItems()).toBe(0);
74
93
  });
94
+
95
+ it('resolves the selected prediction before returning the complete address', async () => {
96
+ const onChange = jest.fn();
97
+ const onSelectAddress = jest.fn();
98
+ const service: AtlasService = {
99
+ searchAddress: jest.fn(),
100
+ predictAddress: jest.fn().mockResolvedValue([
101
+ {
102
+ searchId: 'place-1',
103
+ description: mockAddress.formatted,
104
+ textStructure: {
105
+ mainText: 'Halimon 25',
106
+ secondaryText: 'Tel Mond, IL',
107
+ },
108
+ },
109
+ ]),
110
+ getAddress: jest.fn().mockResolvedValue(mockAddress),
111
+ };
112
+ const { driver } = await setup({
113
+ mockAtlasService: service,
114
+ onChange,
115
+ onSelectAddress,
116
+ });
117
+ await driver.addressInput.clickInput();
118
+ await driver.addressInput.enterText('Halimon');
119
+ await driver.waitForDebounce();
120
+
121
+ expect(await driver.addressInput.getItemMainLabelAt(0)).toBe('Halimon 25');
122
+ expect(await driver.addressInput.getItemSecondaryLabelAt(0)).toBe(
123
+ 'Tel Mond, IL',
124
+ );
125
+ expect(onChange).not.toHaveBeenCalled();
126
+
127
+ await driver.addressInput.clickAtOption(0);
128
+ await driver.flush();
129
+
130
+ expect(service.getAddress).toHaveBeenCalledWith('place-1');
131
+ expect(service.searchAddress).not.toHaveBeenCalled();
132
+ expect(onChange).toHaveBeenCalledWith(mockAddress);
133
+ expect(onSelectAddress).toHaveBeenCalledTimes(1);
134
+ expect(await driver.addressInput.getInputValue()).toBe(
135
+ mockAddress.formatted,
136
+ );
137
+ });
138
+
139
+ it('notifies the owner when the input is emptied', async () => {
140
+ const onChange = jest.fn();
141
+ const { driver } = await setup({ initialValue: mockAddress, onChange });
142
+
143
+ await driver.addressInput.enterText('');
144
+ await driver.flush();
145
+
146
+ expect(onChange).toHaveBeenCalledWith(undefined);
147
+ expect(await driver.addressInput.getInputValue()).toBe('');
148
+ });
149
+
150
+ it('uses Atlas predictions and place details by default', async () => {
151
+ mockPredict.mockResolvedValue({
152
+ predictions: [
153
+ {
154
+ searchId: 'atlas-place',
155
+ description: mockAddress.formatted,
156
+ textStructure: {
157
+ mainText: 'Halimon 25',
158
+ secondaryText: 'Tel Mond, IL',
159
+ },
160
+ },
161
+ ],
162
+ });
163
+ mockGetPlace.mockResolvedValue({ place: { address: mockAddress } });
164
+ const onChange = jest.fn();
165
+ const { driver } = await setup({ onChange });
166
+ await driver.addressInput.clickInput();
167
+ await driver.addressInput.enterText('Halimon');
168
+ await driver.waitForDebounce();
169
+ await driver.addressInput.clickAtOption(0);
170
+ await driver.flush();
171
+
172
+ expect(mockPredict).toHaveBeenCalledWith('Halimon');
173
+ expect(mockGetPlace).toHaveBeenCalledWith('atlas-place');
174
+ expect(onChange).toHaveBeenCalledWith(mockAddress);
175
+ });
176
+
177
+ it('keeps suggestions for the latest query when an older search finishes later', async () => {
178
+ let resolveFirst: (addresses: Address[]) => void = () => {};
179
+ const first = new Promise<Address[]>((resolve) => {
180
+ resolveFirst = resolve;
181
+ });
182
+ const newerAddress = { formatted: 'New York, NY, USA' };
183
+ const service: AtlasService = {
184
+ searchAddress: jest
185
+ .fn()
186
+ .mockReturnValueOnce(first)
187
+ .mockResolvedValueOnce([newerAddress]),
188
+ };
189
+ const { driver } = await setup({ mockAtlasService: service });
190
+ await driver.addressInput.clickInput();
191
+ await driver.addressInput.enterText('New');
192
+ await driver.waitForDebounce();
193
+ await driver.addressInput.enterText('New York');
194
+ await driver.waitForDebounce();
195
+ resolveFirst([mockAddress]);
196
+ await driver.flush();
197
+
198
+ expect(await driver.addressInput.getItemMainLabelAt(0)).toBe('New York');
199
+ expect(await driver.addressInput.getAmountOfItems()).toBe(1);
200
+ });
201
+
202
+ it('does not restore suggestions when a search completes after clearing', async () => {
203
+ let resolveSearch: (addresses: Address[]) => void = () => {};
204
+ const result = new Promise<Address[]>((resolve) => {
205
+ resolveSearch = resolve;
206
+ });
207
+ const { driver } = await setup({
208
+ mockAtlasService: { searchAddress: () => result },
209
+ });
210
+ await driver.addressInput.clickInput();
211
+ await driver.addressInput.enterText('Halimon');
212
+ await driver.waitForDebounce();
213
+ await driver.addressInput.enterText('');
214
+ resolveSearch([mockAddress]);
215
+ await driver.flush();
216
+
217
+ expect(await driver.addressInput.getAmountOfItems()).toBe(0);
218
+ expect(await driver.addressInput.getInputValue()).toBe('');
219
+ });
220
+
221
+ it('does not commit a place lookup that finishes after the input unmounts', async () => {
222
+ let resolveAddress: (address: Address) => void = () => {};
223
+ const result = new Promise<Address>((resolve) => {
224
+ resolveAddress = resolve;
225
+ });
226
+ const onChange = jest.fn();
227
+ const onSelectAddress = jest.fn();
228
+ const service: AtlasService = {
229
+ searchAddress: jest.fn(),
230
+ predictAddress: async () => [
231
+ { searchId: 'place-1', description: mockAddress.formatted },
232
+ ],
233
+ getAddress: () => result,
234
+ };
235
+ const { driver } = await setup({
236
+ mockAtlasService: service,
237
+ onChange,
238
+ onSelectAddress,
239
+ });
240
+ await driver.addressInput.clickInput();
241
+ await driver.addressInput.enterText('Halimon');
242
+ await driver.waitForDebounce();
243
+ await driver.addressInput.clickAtOption(0);
244
+ driver.unmount();
245
+ resolveAddress(mockAddress);
246
+ await driver.flush();
247
+
248
+ expect(onChange).not.toHaveBeenCalled();
249
+ expect(onSelectAddress).not.toHaveBeenCalled();
250
+ });
251
+
252
+ it('does not replace the address when a place lookup finishes after Escape', async () => {
253
+ let resolveAddress: (address: Address) => void = () => {};
254
+ const result = new Promise<Address>((resolve) => {
255
+ resolveAddress = resolve;
256
+ });
257
+ const onChange = jest.fn();
258
+ const service: AtlasService = {
259
+ searchAddress: jest.fn(),
260
+ predictAddress: async () => [
261
+ { searchId: 'new-place', description: 'New York, NY, USA' },
262
+ ],
263
+ getAddress: () => result,
264
+ };
265
+ const { driver } = await setup({
266
+ mockAtlasService: service,
267
+ initialValue: mockAddress,
268
+ onChange,
269
+ });
270
+ await driver.addressInput.clickInput();
271
+ await driver.addressInput.enterText('New York');
272
+ await driver.waitForDebounce();
273
+ await driver.addressInput.clickAtOption(0);
274
+ await driver.pressEscape();
275
+
276
+ resolveAddress({ formatted: 'New York, NY, USA' });
277
+ await driver.flush();
278
+
279
+ expect(onChange).not.toHaveBeenCalled();
280
+ });
75
281
  });
@@ -30,6 +30,8 @@ const charactersLimitRule: FieldValidationRuleConfig = {
30
30
  separator: 'and',
31
31
  outOfBoundsError: (min, max) => `Enter a value from ${min} to ${max}.`,
32
32
  maxBelowMinError: 'Maximum must not be below minimum.',
33
+ exclusiveRulesExplanation:
34
+ "You can't limit character count for specific values.",
33
35
  };
34
36
 
35
37
  const specificValuesRule: FieldValidationRuleConfig = {
@@ -49,6 +51,8 @@ const patternRule: FieldValidationRuleConfig = {
49
51
  tooltip: 'Pattern tooltip',
50
52
  inputLabel: 'Regular expression',
51
53
  invalidError: 'Enter a valid regular expression.',
54
+ exclusiveRulesExplanation:
55
+ "You can't add pattern validation for specific values.",
52
56
  };
53
57
 
54
58
  const allRules = [
@@ -68,6 +72,7 @@ interface RenderInputOptions {
68
72
  readOnly?: boolean;
69
73
  invalid?: boolean;
70
74
  disabledRules?: readonly FieldValidationRuleConfig['type'][];
75
+ exclusiveSpecificValues?: boolean;
71
76
  }
72
77
 
73
78
  const renderInput = (
@@ -77,6 +82,7 @@ const renderInput = (
77
82
  readOnly,
78
83
  invalid,
79
84
  disabledRules,
85
+ exclusiveSpecificValues,
80
86
  }: RenderInputOptions = {},
81
87
  ) => {
82
88
  const onChange = jest.fn<void, [FieldValidationSettingsValue]>();
@@ -91,6 +97,7 @@ const renderInput = (
91
97
  readOnly={readOnly}
92
98
  invalid={invalid}
93
99
  disabledRules={disabledRules}
100
+ exclusiveSpecificValues={exclusiveSpecificValues}
94
101
  onChange={(nextData) => {
95
102
  onChange(nextData);
96
103
  setData(nextData);
@@ -337,6 +344,86 @@ describe('FieldValidationSettingsInput', () => {
337
344
  expect(await driver.getPatternToggle().isDisabled()).toBe(true);
338
345
  });
339
346
 
347
+ it('explains exclusivity under each rule that specific values block', async () => {
348
+ const { driver } = renderInput({ specificValues: [] });
349
+
350
+ expect(
351
+ await driver.getExclusiveRulesExplanation('characters-limit').text(),
352
+ ).toBe("You can't limit character count for specific values.");
353
+ expect(await driver.getExclusiveRulesExplanation('pattern').text()).toBe(
354
+ "You can't add pattern validation for specific values.",
355
+ );
356
+ expect(
357
+ await driver.getExclusiveRulesExplanation('specific-values').exists(),
358
+ ).toBe(false);
359
+ });
360
+
361
+ it('explains exclusivity under specific values when a character limit blocks it', async () => {
362
+ const { driver } = renderInput({ charactersLimit: {} });
363
+
364
+ expect(
365
+ await driver.getExclusiveRulesExplanation('specific-values').text(),
366
+ ).toBe('Disable the active rule first.');
367
+ expect(
368
+ await driver.getExclusiveRulesExplanation('characters-limit').exists(),
369
+ ).toBe(false);
370
+ });
371
+
372
+ it('falls back to the specific-values explanation exactly once for legacy configs', async () => {
373
+ const { driver, container } = renderInput(
374
+ { specificValues: [] },
375
+ {
376
+ rules: [
377
+ { ...charactersLimitRule, exclusiveRulesExplanation: undefined },
378
+ specificValuesRule,
379
+ { ...patternRule, exclusiveRulesExplanation: undefined },
380
+ ],
381
+ },
382
+ );
383
+
384
+ expect(await driver.hasExclusiveRulesExplanation()).toBe(true);
385
+ expect(
386
+ Array.from(
387
+ container.querySelectorAll('[data-hook$="exclusive-help"]'),
388
+ ).map((node) => node.textContent),
389
+ ).toEqual(['Disable the active rule first.']);
390
+ });
391
+
392
+ it('omits an exclusivity explanation when no rule config supplies one', async () => {
393
+ const { driver } = renderInput(
394
+ { specificValues: [] },
395
+ {
396
+ rules: [
397
+ { ...charactersLimitRule, exclusiveRulesExplanation: undefined },
398
+ { ...specificValuesRule, exclusiveRulesExplanation: undefined },
399
+ ],
400
+ },
401
+ );
402
+
403
+ expect(await driver.getCharactersLimitToggle().isDisabled()).toBe(true);
404
+ expect(await driver.hasExclusiveRulesExplanation()).toBe(false);
405
+ });
406
+
407
+ it('keeps every rule toggle enabled when specific values are not exclusive', async () => {
408
+ const { driver } = renderInput(
409
+ { specificValues: [] },
410
+ { exclusiveSpecificValues: false },
411
+ );
412
+
413
+ expect(await driver.getSpecificValuesToggle().isDisabled()).toBe(false);
414
+ expect(await driver.getCharactersLimitToggle().isDisabled()).toBe(false);
415
+ expect(await driver.getPatternToggle().isDisabled()).toBe(false);
416
+ });
417
+
418
+ it('does not explain exclusivity when specific values are not exclusive', async () => {
419
+ const { driver } = renderInput(
420
+ { charactersLimit: {} },
421
+ { exclusiveSpecificValues: false },
422
+ );
423
+
424
+ expect(await driver.hasExclusiveRulesExplanation()).toBe(false);
425
+ });
426
+
340
427
  it('keeps active conflicting rules enabled so loaded data can be repaired', async () => {
341
428
  const { driver, onChange } = renderInput({
342
429
  charactersLimit: {},
@@ -109,6 +109,69 @@ describe('FormUrl field', () => {
109
109
  expect(await driver.tk.hasError()).toBe(false);
110
110
  });
111
111
 
112
+ it('shows a min-length error when the URL is shorter than minLength', async () => {
113
+ const driver = setup({
114
+ field: makeField({
115
+ type: 'URL',
116
+ validation: { required: false, stringLengthRange: { minLength: 25 } },
117
+ }),
118
+ });
119
+
120
+ await driver.tk.enterText('https://wix.com');
121
+
122
+ expect(await driver.tk.hasError()).toBe(true);
123
+ expect(await driver.tk.getErrorMessage()).toBe(
124
+ 'cairo.fieldValidation.minCharacter{charMin=25}',
125
+ );
126
+ });
127
+
128
+ it('shows a max-length error when the URL is longer than maxLength', async () => {
129
+ const driver = setup({
130
+ field: makeField({
131
+ type: 'URL',
132
+ validation: { required: false, stringLengthRange: { maxLength: 20 } },
133
+ }),
134
+ });
135
+
136
+ await driver.tk.enterText('https://example.com/a/very/long/path');
137
+
138
+ expect(await driver.tk.hasError()).toBe(true);
139
+ expect(await driver.tk.getErrorMessage()).toBe(
140
+ 'cairo.fieldValidation.maxCharacter{charMax=20}',
141
+ );
142
+ });
143
+
144
+ it('accepts a URL inside the allowed length range', async () => {
145
+ const driver = setup({
146
+ field: makeField({
147
+ type: 'URL',
148
+ validation: {
149
+ required: false,
150
+ stringLengthRange: { minLength: 10, maxLength: 40 },
151
+ },
152
+ }),
153
+ });
154
+
155
+ await driver.tk.enterText('https://example.com');
156
+
157
+ expect(await driver.tk.hasError()).toBe(false);
158
+ });
159
+
160
+ it('reports the malformed-URL error before the length error', async () => {
161
+ const driver = setup({
162
+ field: makeField({
163
+ type: 'URL',
164
+ validation: { required: false, stringLengthRange: { minLength: 25 } },
165
+ }),
166
+ });
167
+
168
+ await driver.tk.enterText('not a url');
169
+
170
+ expect(await driver.tk.getErrorMessage()).toBe(
171
+ 'cairo.customFields.url-input.error',
172
+ );
173
+ });
174
+
112
175
  it('reports the malformed-URL error before the pattern error', async () => {
113
176
  const driver = setup({
114
177
  field: makeField({
@@ -2,6 +2,7 @@ import '../../../testkit/test-polyfills';
2
2
 
3
3
  import type { Field } from '@wix/bex-core';
4
4
 
5
+ import { Z_INDEX_MODAL_POPOVER } from '../../../components/common';
5
6
  import { FormVideo } from '../../../components/video/form-video';
6
7
  import { FormVideoDriver } from '../../../components/video/form-video.driver';
7
8
  import { FormFieldDriver, makeField } from '../../form-field.driver';
@@ -82,6 +83,22 @@ describe('FormVideo field', () => {
82
83
  );
83
84
  });
84
85
 
86
+ it('raises the more-actions menu above modal content', async () => {
87
+ const field = makeField({ type: 'VIDEO' });
88
+ const driver = setup({ field, initialValue: VALID_VIDEO_URI });
89
+
90
+ await driver.tk.clickMore();
91
+
92
+ const deleteAction = document.querySelector(
93
+ `[data-hook="video-delete-${field.id}"]`,
94
+ );
95
+ expect(deleteAction).toBeTruthy();
96
+ expect(deleteAction!.closest('[data-zindex]')).toHaveAttribute(
97
+ 'data-zindex',
98
+ String(Z_INDEX_MODAL_POPOVER),
99
+ );
100
+ });
101
+
85
102
  // The empty state is the only one that can fail `required`, so its add
86
103
  // button is what react-hook-form should land on.
87
104
  it('focuses the add button when a required value blocks submit', async () => {
@@ -488,6 +488,52 @@ describe('controlled inputs', () => {
488
488
  expect(onChange).toHaveBeenCalledWith(undefined);
489
489
  });
490
490
 
491
+ // The testkit factory resolves a dataHook once, when the driver is built, so
492
+ // a driver made before the click keeps answering about the old render. Each
493
+ // assertion below uses a driver created after the render it describes.
494
+ it('shows the audio loader inside the add button while the picker is open', async () => {
495
+ let resolvePick: (value: unknown) => void = () => {};
496
+ const openMediaManager = jest.fn().mockImplementation(
497
+ () =>
498
+ new Promise((resolve) => {
499
+ resolvePick = resolve;
500
+ }),
501
+ );
502
+ const { container } = render(
503
+ <ControlledAudioInput
504
+ data={undefined}
505
+ onChange={jest.fn()}
506
+ dataHook="audio-loading"
507
+ />,
508
+ { wrapper: withMediaManager(openMediaManager) },
509
+ );
510
+
511
+ await getInput(container, 'audio-loading').audio.clickAddButton();
512
+ await act(async () => {});
513
+
514
+ expect(openMediaManager).toHaveBeenCalledTimes(1);
515
+
516
+ const fieldBox = container.querySelector<HTMLElement>(
517
+ '[data-hook="audio-loading"]',
518
+ );
519
+ expect(fieldBox).not.toBeNull();
520
+ expect(
521
+ fieldBox!.querySelector('[data-hook="fromAudioInput.loader"]'),
522
+ ).not.toBeNull();
523
+
524
+ const loading = getInput(container, 'audio-loading').audio;
525
+ expect(await loading.isLoading()).toBe(true);
526
+ expect(await loading.getAddButtonText()).toBe('');
527
+
528
+ await act(async () => {
529
+ resolvePick({ items: [] });
530
+ });
531
+
532
+ const settled = getInput(container, 'audio-loading').audio;
533
+ expect(await settled.isLoading()).toBe(false);
534
+ expect(await settled.getAddButtonText()).toBe('CMS.audio.button.add');
535
+ });
536
+
491
537
  it('disables the audio replace and delete actions when readOnly', async () => {
492
538
  const { container } = render(
493
539
  <ControlledAudioInput
@@ -5,7 +5,7 @@ import { classesRuntime, statesRuntime } from "../../../stylable-esm-runtime.js"
5
5
 
6
6
 
7
7
 
8
- var _namespace_ = "addressinput135427117";
8
+ var _namespace_ = "addressinput549716124";
9
9
  var _style_ = classesRuntime.bind(null, _namespace_);
10
10
 
11
11
  export var cssStates = statesRuntime.bind(null, _namespace_);
@@ -13,7 +13,7 @@ export var style: import("@stylable/runtime").STFunction = _style_;
13
13
  export var st: import("@stylable/runtime").STFunction = _style_;
14
14
 
15
15
  export var namespace = _namespace_;
16
- export var classes = {"root":"addressinput135427117__root","tableAddressInput":"addressinput135427117__tableAddressInput"};
16
+ export var classes = {"root":"addressinput549716124__root","tableAddressInput":"addressinput549716124__tableAddressInput"};
17
17
  export var keyframes = {};
18
18
  export var layers = {};
19
19
  export var containers = {};