@qoretechnologies/reqraft 0.10.25 → 0.10.26

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 (310) hide show
  1. package/.claude/CLAUDE.md +52 -23
  2. package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
  3. package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
  4. package/dist/components/FocusedEditing/index.d.ts +9 -1
  5. package/dist/components/FocusedEditing/index.d.ts.map +1 -1
  6. package/dist/components/FocusedEditing/index.js +2 -2
  7. package/dist/components/FocusedEditing/index.js.map +1 -1
  8. package/dist/components/composer/Composer.d.ts +108 -0
  9. package/dist/components/composer/Composer.d.ts.map +1 -0
  10. package/dist/components/composer/Composer.js +238 -0
  11. package/dist/components/composer/Composer.js.map +1 -0
  12. package/dist/components/composer/helpers.d.ts +21 -0
  13. package/dist/components/composer/helpers.d.ts.map +1 -0
  14. package/dist/components/composer/helpers.js +75 -0
  15. package/dist/components/composer/helpers.js.map +1 -0
  16. package/dist/components/composer/index.d.ts +3 -0
  17. package/dist/components/composer/index.d.ts.map +1 -0
  18. package/dist/components/composer/index.js +19 -0
  19. package/dist/components/composer/index.js.map +1 -0
  20. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  21. package/dist/components/form/engine/CompactRow.js +307 -110
  22. package/dist/components/form/engine/CompactRow.js.map +1 -1
  23. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  24. package/dist/components/form/engine/CompactToolbar.js +123 -106
  25. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  26. package/dist/components/form/engine/FormEngine.d.ts +107 -11
  27. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  28. package/dist/components/form/engine/FormEngine.js +618 -179
  29. package/dist/components/form/engine/FormEngine.js.map +1 -1
  30. package/dist/components/form/engine/compactRowContext.d.ts +9 -0
  31. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  32. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  33. package/dist/components/form/engine/compactRowStyles.d.ts +7 -3
  34. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  35. package/dist/components/form/engine/compactRowStyles.js +88 -51
  36. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  37. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  38. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  39. package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
  40. package/dist/components/form/engine/optionActions.d.ts +28 -0
  41. package/dist/components/form/engine/optionActions.d.ts.map +1 -0
  42. package/dist/components/form/engine/optionActions.js +18 -0
  43. package/dist/components/form/engine/optionActions.js.map +1 -0
  44. package/dist/components/form/engine/readFirst.d.ts +63 -2
  45. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  46. package/dist/components/form/engine/readFirst.js +121 -6
  47. package/dist/components/form/engine/readFirst.js.map +1 -1
  48. package/dist/components/form/engine/rendererTypes.d.ts +35 -0
  49. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
  50. package/dist/components/form/engine/rendererTypes.js +63 -0
  51. package/dist/components/form/engine/rendererTypes.js.map +1 -0
  52. package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
  53. package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
  54. package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
  55. package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
  56. package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
  57. package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
  58. package/dist/components/form/engine/variants/VariantCards.js +80 -0
  59. package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
  60. package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
  61. package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
  62. package/dist/components/form/engine/variants/VariantFocus.js +138 -0
  63. package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
  64. package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
  65. package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
  66. package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
  67. package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
  68. package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
  69. package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
  70. package/dist/components/form/engine/variants/focusDemo.js +139 -0
  71. package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
  72. package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
  73. package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
  74. package/dist/components/form/engine/variants/variantModel.js +133 -0
  75. package/dist/components/form/engine/variants/variantModel.js.map +1 -0
  76. package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
  77. package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
  78. package/dist/components/form/engine/variants/variantParts.js +191 -0
  79. package/dist/components/form/engine/variants/variantParts.js.map +1 -0
  80. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  81. package/dist/components/form/expressions/builder/index.js +13 -2
  82. package/dist/components/form/expressions/builder/index.js.map +1 -1
  83. package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
  84. package/dist/components/form/expressions/useExpressions.js +4 -1
  85. package/dist/components/form/expressions/useExpressions.js.map +1 -1
  86. package/dist/components/form/fields/Field.d.ts.map +1 -1
  87. package/dist/components/form/fields/Field.js +5 -3
  88. package/dist/components/form/fields/Field.js.map +1 -1
  89. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
  90. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  91. package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
  92. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  93. package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
  94. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  95. package/dist/components/form/fields/auto/AutoFormField.js +42 -8
  96. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  97. package/dist/components/form/fields/binary/Binary.d.ts +15 -0
  98. package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
  99. package/dist/components/form/fields/binary/Binary.js +74 -0
  100. package/dist/components/form/fields/binary/Binary.js.map +1 -0
  101. package/dist/components/form/fields/boolean/Boolean.js +1 -1
  102. package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
  103. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  104. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
  105. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  106. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  107. package/dist/components/form/fields/select/Select.js +29 -12
  108. package/dist/components/form/fields/select/Select.js.map +1 -1
  109. package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
  110. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  111. package/dist/components/form/fields/select/SelectCollection.js +52 -16
  112. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  113. package/dist/components/form/index.d.ts +2 -0
  114. package/dist/components/form/index.d.ts.map +1 -1
  115. package/dist/components/form/index.js +2 -0
  116. package/dist/components/form/index.js.map +1 -1
  117. package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
  118. package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
  119. package/dist/components/imageLightbox/ImageLightbox.js +169 -0
  120. package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
  121. package/dist/components/imageLightbox/index.d.ts +2 -0
  122. package/dist/components/imageLightbox/index.d.ts.map +1 -0
  123. package/dist/components/imageLightbox/index.js +18 -0
  124. package/dist/components/imageLightbox/index.js.map +1 -0
  125. package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
  126. package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
  127. package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
  128. package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
  129. package/dist/components/searchFilterBar/index.d.ts +2 -0
  130. package/dist/components/searchFilterBar/index.d.ts.map +1 -0
  131. package/dist/components/searchFilterBar/index.js +18 -0
  132. package/dist/components/searchFilterBar/index.js.map +1 -0
  133. package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
  134. package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
  135. package/dist/components/supportTicket/AttachmentChips.js +122 -0
  136. package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
  137. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
  138. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
  139. package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
  140. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
  141. package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
  142. package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
  143. package/dist/components/supportTicket/ReferencePicker.js +295 -0
  144. package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
  145. package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
  146. package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
  147. package/dist/components/supportTicket/TicketHeader.js +149 -0
  148. package/dist/components/supportTicket/TicketHeader.js.map +1 -0
  149. package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
  150. package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
  151. package/dist/components/supportTicket/TicketMetaTags.js +21 -0
  152. package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
  153. package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
  154. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
  155. package/dist/components/supportTicket/TicketReplyBox.js +115 -0
  156. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
  157. package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
  158. package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
  159. package/dist/components/supportTicket/attachmentAccess.js +132 -0
  160. package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
  161. package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
  162. package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
  163. package/dist/components/supportTicket/fileToAttachment.js +28 -0
  164. package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
  165. package/dist/components/supportTicket/index.d.ts +11 -0
  166. package/dist/components/supportTicket/index.d.ts.map +1 -0
  167. package/dist/components/supportTicket/index.js +27 -0
  168. package/dist/components/supportTicket/index.js.map +1 -0
  169. package/dist/components/supportTicket/meta.d.ts +71 -0
  170. package/dist/components/supportTicket/meta.d.ts.map +1 -0
  171. package/dist/components/supportTicket/meta.js +115 -0
  172. package/dist/components/supportTicket/meta.js.map +1 -0
  173. package/dist/components/supportTicket/ticketModel.d.ts +102 -0
  174. package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
  175. package/dist/components/supportTicket/ticketModel.js +3 -0
  176. package/dist/components/supportTicket/ticketModel.js.map +1 -0
  177. package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
  178. package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
  179. package/dist/components/ticketReferences/TicketReferences.js +492 -0
  180. package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
  181. package/dist/components/ticketReferences/index.d.ts +3 -0
  182. package/dist/components/ticketReferences/index.d.ts.map +1 -0
  183. package/dist/components/ticketReferences/index.js +6 -0
  184. package/dist/components/ticketReferences/index.js.map +1 -0
  185. package/dist/components/ticketThread/TicketThread.d.ts +57 -0
  186. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
  187. package/dist/components/ticketThread/TicketThread.js +299 -0
  188. package/dist/components/ticketThread/TicketThread.js.map +1 -0
  189. package/dist/components/ticketThread/index.d.ts +3 -0
  190. package/dist/components/ticketThread/index.d.ts.map +1 -0
  191. package/dist/components/ticketThread/index.js +7 -0
  192. package/dist/components/ticketThread/index.js.map +1 -0
  193. package/dist/helpers/common.d.ts +5 -0
  194. package/dist/helpers/common.d.ts.map +1 -1
  195. package/dist/helpers/common.js +20 -1
  196. package/dist/helpers/common.js.map +1 -1
  197. package/dist/helpers/scrollbar.d.ts +2 -0
  198. package/dist/helpers/scrollbar.d.ts.map +1 -0
  199. package/dist/helpers/scrollbar.js +25 -0
  200. package/dist/helpers/scrollbar.js.map +1 -0
  201. package/dist/index.d.ts +8 -1
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +10 -1
  204. package/dist/index.js.map +1 -1
  205. package/dist/providers/StorageProvider.d.ts +13 -0
  206. package/dist/providers/StorageProvider.d.ts.map +1 -1
  207. package/dist/providers/StorageProvider.js +29 -5
  208. package/dist/providers/StorageProvider.js.map +1 -1
  209. package/package.json +6 -5
  210. package/src/components/FocusedEditing/index.tsx +17 -2
  211. package/src/components/composer/Composer.stories.tsx +238 -0
  212. package/src/components/composer/Composer.tsx +545 -0
  213. package/src/components/composer/helpers.ts +74 -0
  214. package/src/components/composer/index.ts +2 -0
  215. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
  216. package/src/components/form/engine/CompactRow.tsx +561 -283
  217. package/src/components/form/engine/CompactToolbar.tsx +216 -182
  218. package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
  219. package/src/components/form/engine/FormEngine.tsx +886 -170
  220. package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
  221. package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
  222. package/src/components/form/engine/compactRowContext.ts +14 -0
  223. package/src/components/form/engine/compactRowStyles.ts +273 -146
  224. package/src/components/form/engine/compactToolbarContext.ts +1 -0
  225. package/src/components/form/engine/optionActions.ts +40 -0
  226. package/src/components/form/engine/readFirst.ts +169 -10
  227. package/src/components/form/engine/rendererTypes.ts +55 -0
  228. package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
  229. package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
  230. package/src/components/form/engine/variants/VariantCards.tsx +212 -0
  231. package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
  232. package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
  233. package/src/components/form/engine/variants/focusDemo.ts +145 -0
  234. package/src/components/form/engine/variants/variantModel.ts +216 -0
  235. package/src/components/form/engine/variants/variantParts.tsx +313 -0
  236. package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
  237. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
  238. package/src/components/form/expressions/builder/index.tsx +12 -1
  239. package/src/components/form/expressions/useExpressions.ts +4 -1
  240. package/src/components/form/fields/Field.stories.tsx +40 -0
  241. package/src/components/form/fields/Field.tsx +10 -1
  242. package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
  243. package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
  244. package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
  245. package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
  246. package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
  247. package/src/components/form/fields/binary/Binary.tsx +81 -0
  248. package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
  249. package/src/components/form/fields/boolean/Boolean.tsx +1 -1
  250. package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
  251. package/src/components/form/fields/color/Color.stories.tsx +30 -0
  252. package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
  253. package/src/components/form/fields/date/Date.stories.tsx +22 -0
  254. package/src/components/form/fields/file/File.stories.tsx +48 -0
  255. package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
  256. package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
  257. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
  258. package/src/components/form/fields/number/Number.stories.tsx +31 -0
  259. package/src/components/form/fields/object/Object.stories.tsx +88 -0
  260. package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
  261. package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
  262. package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
  263. package/src/components/form/fields/select/Select.stories.tsx +108 -0
  264. package/src/components/form/fields/select/Select.tsx +38 -12
  265. package/src/components/form/fields/select/SelectCollection.tsx +78 -17
  266. package/src/components/form/fields/string/String.stories.tsx +16 -0
  267. package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
  268. package/src/components/form/fields/url/Url.stories.tsx +24 -0
  269. package/src/components/form/index.tsx +2 -0
  270. package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
  271. package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
  272. package/src/components/imageLightbox/index.ts +1 -0
  273. package/src/components/log/Log.stories.tsx +96 -0
  274. package/src/components/menu/Menu.stories.tsx +54 -0
  275. package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
  276. package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
  277. package/src/components/searchFilterBar/index.ts +1 -0
  278. package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
  279. package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
  280. package/src/components/supportTicket/AttachmentChips.tsx +147 -0
  281. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
  282. package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
  283. package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
  284. package/src/components/supportTicket/ReferencePicker.tsx +563 -0
  285. package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
  286. package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
  287. package/src/components/supportTicket/TicketHeader.tsx +247 -0
  288. package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
  289. package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
  290. package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
  291. package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
  292. package/src/components/supportTicket/attachmentAccess.ts +97 -0
  293. package/src/components/supportTicket/fileToAttachment.ts +24 -0
  294. package/src/components/supportTicket/index.ts +10 -0
  295. package/src/components/supportTicket/meta.ts +185 -0
  296. package/src/components/supportTicket/ticketModel.ts +131 -0
  297. package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
  298. package/src/components/ticketReferences/TicketReferences.tsx +886 -0
  299. package/src/components/ticketReferences/index.ts +2 -0
  300. package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
  301. package/src/components/ticketThread/TicketThread.tsx +704 -0
  302. package/src/components/ticketThread/index.ts +6 -0
  303. package/src/helpers/common.ts +19 -0
  304. package/src/helpers/scrollbar.ts +32 -0
  305. package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
  306. package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
  307. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
  308. package/src/index.tsx +8 -1
  309. package/src/providers/StorageProvider.tsx +30 -4
  310. package/src/stores/currentUser/currentUser.stories.tsx +49 -0
@@ -0,0 +1,122 @@
1
+ import { ReqoreControlGroup } from '@qoretechnologies/reqore';
2
+ import { StoryObj } from '@storybook/react-vite';
3
+ import { useState } from 'react';
4
+ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
5
+
6
+ import { StoryMeta } from '../../../../types';
7
+ import { ReqraftBinaryFormField } from './Binary';
8
+
9
+ const meta = {
10
+ component: ReqraftBinaryFormField,
11
+ title: 'Components/Form/Binary',
12
+ args: {
13
+ onChange: fn(),
14
+ 'aria-label': 'Binary',
15
+ },
16
+ render(args) {
17
+ const [value, setValue] = useState(args.value);
18
+
19
+ return (
20
+ <ReqoreControlGroup>
21
+ <ReqraftBinaryFormField
22
+ {...args}
23
+ value={value}
24
+ onChange={(value) => {
25
+ args.onChange?.(value);
26
+ setValue(value);
27
+ }}
28
+ />
29
+ </ReqoreControlGroup>
30
+ );
31
+ },
32
+ } as StoryMeta<typeof ReqraftBinaryFormField>;
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof meta>;
36
+
37
+ export const WithValue: Story = {
38
+ args: {
39
+ value: 'cGFzc3dvcmQ=',
40
+ },
41
+ parameters: {
42
+ docs: {
43
+ description: {
44
+ story:
45
+ 'Renders the Binary field pre-populated with a base64 payload — the textarea shows the encoded string.',
46
+ },
47
+ },
48
+ },
49
+ async play({ canvasElement }) {
50
+ const canvas = within(canvasElement);
51
+ const textarea = canvas.getByLabelText('Binary');
52
+
53
+ await expect(textarea).toBeInTheDocument();
54
+ await expect(textarea).toHaveValue('cGFzc3dvcmQ=');
55
+ },
56
+ };
57
+
58
+ export const TypingUpdatesValue: Story = {
59
+ parameters: {
60
+ docs: {
61
+ description: {
62
+ story:
63
+ 'Renders the empty Binary field. Typing a base64 string into the textarea updates the value and fires onChange after the debounce.',
64
+ },
65
+ },
66
+ },
67
+ async play({ canvasElement, args }) {
68
+ const canvas = within(canvasElement);
69
+ const textarea = canvas.getByLabelText('Binary');
70
+
71
+ await userEvent.type(textarea, 'YWJj');
72
+ await expect(textarea).toHaveValue('YWJj');
73
+ await waitFor(() => expect(args.onChange).toHaveBeenLastCalledWith('YWJj'), {
74
+ timeout: 500,
75
+ });
76
+ },
77
+ };
78
+
79
+ export const UploadEncodesFileAsBase64: Story = {
80
+ parameters: {
81
+ docs: {
82
+ description: {
83
+ story:
84
+ 'Renders the Binary field with its file drop zone. Uploading a file encodes it as base64 (data-URL prefix stripped) and fires onChange with the encoded string.',
85
+ },
86
+ },
87
+ },
88
+ async play({ canvasElement, args }) {
89
+ const file = new File(['hello'], 'test.bin', { type: 'application/octet-stream' });
90
+ // the hidden file input rendered by the reused ReqraftFileFormField drop zone
91
+ const input = canvasElement.querySelector('input[type="file"]') as HTMLInputElement;
92
+
93
+ await expect(input).toBeInTheDocument();
94
+ await userEvent.upload(input, file);
95
+
96
+ // base64 of "hello" is "aGVsbG8="; the data: URL prefix must be stripped
97
+ await waitFor(() => expect(args.onChange).toHaveBeenLastCalledWith('aGVsbG8='), {
98
+ timeout: 1000,
99
+ });
100
+ },
101
+ };
102
+
103
+ export const Disabled: Story = {
104
+ args: {
105
+ value: 'cGFzc3dvcmQ=',
106
+ disabled: true,
107
+ },
108
+ parameters: {
109
+ docs: {
110
+ description: {
111
+ story:
112
+ 'Renders the Binary field with a base64 value but disabled — the textarea is non-interactive.',
113
+ },
114
+ },
115
+ },
116
+ async play({ canvasElement }) {
117
+ const canvas = within(canvasElement);
118
+ const textarea = canvas.getByLabelText('Binary');
119
+
120
+ await expect(textarea).toBeDisabled();
121
+ },
122
+ };
@@ -0,0 +1,81 @@
1
+ import { ReqoreControlGroup, ReqoreTextarea } from '@qoretechnologies/reqore';
2
+ import { IReqoreTextareaProps } from '@qoretechnologies/reqore/dist/components/Textarea';
3
+ import { ChangeEvent, memo, useCallback, useEffect, useState } from 'react';
4
+ import { useDebounce } from 'react-use';
5
+ import { IFileFormFieldValue, ReqraftFileFormField } from '../file/File';
6
+
7
+ export interface IReqraftBinaryFormFieldProps extends Omit<IReqoreTextareaProps, 'onChange' | 'value'> {
8
+ value?: string;
9
+ onChange?: (value: string) => void;
10
+ }
11
+
12
+ /**
13
+ * Strip a `data:<mime-type>;base64,` prefix so the emitted value carries raw base64, matching the
14
+ * canonical binary wire form the Qorus server expects. `ReqraftFileFormField` reads uploads with
15
+ * `readAsDataURL`, so its `content` always carries that prefix.
16
+ */
17
+ const stripDataUrlPrefix = (dataUrl: string): string => {
18
+ const commaIndex = dataUrl.indexOf(',');
19
+ return commaIndex === -1 ? dataUrl : dataUrl.slice(commaIndex + 1);
20
+ };
21
+
22
+ /**
23
+ * A field for binary values encoded as base64. The value can be typed or pasted directly into the
24
+ * textarea, or populated by uploading a file — the upload reuses {@link ReqraftFileFormField} (the
25
+ * shared Reqraft file picker / drop zone) rather than a bespoke dropzone; its data-URL content is
26
+ * decoded to raw base64 for the field value.
27
+ */
28
+ export const ReqraftBinaryFormField = memo(
29
+ ({ value, onChange, ...rest }: IReqraftBinaryFormFieldProps) => {
30
+ const [localValue, setLocalValue] = useState<string>(value ?? '');
31
+
32
+ useEffect(() => {
33
+ if (value !== localValue) {
34
+ setLocalValue(value ?? '');
35
+ }
36
+ }, [value]);
37
+
38
+ useDebounce(
39
+ () => {
40
+ if (localValue !== value) {
41
+ onChange?.(localValue);
42
+ }
43
+ },
44
+ 100,
45
+ [localValue, onChange]
46
+ );
47
+
48
+ const handleChange = useCallback((event: ChangeEvent<HTMLTextAreaElement>): void => {
49
+ setLocalValue(event.target.value);
50
+ }, []);
51
+
52
+ const handleFileChange = useCallback(
53
+ (file?: IFileFormFieldValue) => {
54
+ const base64 = stripDataUrlPrefix(file?.content ?? '');
55
+ setLocalValue(base64);
56
+ onChange?.(base64);
57
+ },
58
+ [onChange]
59
+ );
60
+
61
+ return (
62
+ <ReqoreControlGroup vertical fluid>
63
+ <ReqoreTextarea
64
+ scaleWithContent
65
+ fluid
66
+ value={localValue}
67
+ onChange={handleChange}
68
+ onClearClick={() => {
69
+ setLocalValue('');
70
+ onChange?.('');
71
+ }}
72
+ placeholder='Base64-encoded binary value'
73
+ {...rest}
74
+ />
75
+ <ReqraftFileFormField disabled={rest.disabled} onChange={handleFileChange} />
76
+ </ReqoreControlGroup>
77
+ );
78
+ }
79
+ );
80
+
81
+ export default ReqraftBinaryFormField;
@@ -34,6 +34,14 @@ export const Checked: Story = {
34
34
  args: {
35
35
  checked: true,
36
36
  },
37
+ parameters: {
38
+ docs: {
39
+ description: {
40
+ story:
41
+ 'Renders the Boolean field in the checked state. Clicking the toggle flips it to unchecked and fires onChange with false.',
42
+ },
43
+ },
44
+ },
37
45
  async play({ canvasElement, args }) {
38
46
  const canvas = within(canvasElement);
39
47
 
@@ -49,6 +57,14 @@ export const Unchecked: Story = {
49
57
  args: {
50
58
  checked: false,
51
59
  },
60
+ parameters: {
61
+ docs: {
62
+ description: {
63
+ story:
64
+ 'Renders the Boolean field in the unchecked state. Clicking the toggle flips it to checked and fires onChange with true.',
65
+ },
66
+ },
67
+ },
52
68
  async play({ canvasElement, args }) {
53
69
  const canvas = within(canvasElement);
54
70
 
@@ -59,9 +75,51 @@ export const Unchecked: Story = {
59
75
  },
60
76
  };
61
77
 
78
+ export const Unset: Story = {
79
+ args: {
80
+ checked: undefined,
81
+ },
82
+ parameters: {
83
+ docs: {
84
+ description: {
85
+ story:
86
+ 'Renders the Boolean field without a default choice. The switch remains visibly unset until the user explicitly chooses Yes or No.',
87
+ },
88
+ },
89
+ },
90
+ };
91
+
92
+ export const UnsetSelectsYes: Story = {
93
+ args: {
94
+ checked: undefined,
95
+ },
96
+ parameters: {
97
+ docs: {
98
+ description: {
99
+ story:
100
+ 'Verifies that an unset Boolean field saves the first explicit Yes selection instead of treating the missing value as No.',
101
+ },
102
+ },
103
+ },
104
+ async play({ canvasElement, args }) {
105
+ const canvas = within(canvasElement);
106
+
107
+ await userEvent.click(canvas.getByLabelText('Boolean'));
108
+ await expect(args.onChange).toHaveBeenLastCalledWith(true);
109
+ },
110
+ };
111
+
62
112
  export const Disabled: Story = {
63
113
  args: {
64
114
  checked: true,
65
115
  disabled: true,
66
116
  },
117
+ parameters: {
118
+ docs: {
119
+ description: {
120
+ story:
121
+ 'Renders the Boolean field checked and disabled — the toggle is visible but non-interactive.',
122
+ },
123
+ },
124
+ },
67
125
  };
@@ -18,7 +18,7 @@ export const BooleanFormField = ({
18
18
 
19
19
  return (
20
20
  <ReqoreCheckbox
21
- checked={checked ?? false}
21
+ checked={checked}
22
22
  onClick={toggle}
23
23
  asSwitch
24
24
  onText='Yes'
@@ -30,6 +30,14 @@ export default meta;
30
30
  type Story = StoryObj<typeof meta>;
31
31
 
32
32
  export const Default: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story:
37
+ 'Renders the ByteSize field with no value. Typing a number into the amount input fires onChange with the composed byte-size expression.',
38
+ },
39
+ },
40
+ },
33
41
  play: async ({ canvasElement, args }) => {
34
42
  const canvas = within(canvasElement);
35
43
  const input = canvas.getByLabelText('Byte size amount');
@@ -43,6 +51,14 @@ export const WithValue: Story = {
43
51
  args: {
44
52
  value: '512MiB',
45
53
  },
54
+ parameters: {
55
+ docs: {
56
+ description: {
57
+ story:
58
+ 'Renders the ByteSize field pre-populated with "512MiB" — the amount and unit are split across the number input and the MiB unit selector.',
59
+ },
60
+ },
61
+ },
46
62
  play: async ({ canvasElement }) => {
47
63
  const canvas = within(canvasElement);
48
64
  await expect(await canvas.findByDisplayValue('512')).toBeInTheDocument();
@@ -55,6 +71,14 @@ export const ReadOnly: Story = {
55
71
  value: '512MiB',
56
72
  readOnly: true,
57
73
  },
74
+ parameters: {
75
+ docs: {
76
+ description: {
77
+ story:
78
+ 'Renders the ByteSize field with "512MiB" in read-only mode — the amount input is marked readonly and rejects further edits.',
79
+ },
80
+ },
81
+ },
58
82
  play: async ({ canvasElement }) => {
59
83
  const canvas = within(canvasElement);
60
84
  const input = await canvas.findByDisplayValue('512');
@@ -34,6 +34,14 @@ export const WithValue: Story = {
34
34
  args: {
35
35
  value: { r: 100, g: 149, b: 237, a: 1 },
36
36
  },
37
+ parameters: {
38
+ docs: {
39
+ description: {
40
+ story:
41
+ 'Renders the Color field with a cornflower-blue RGBA value pre-selected in the sketch picker.',
42
+ },
43
+ },
44
+ },
37
45
  async play({ canvasElement }) {
38
46
  const picker = canvasElement.querySelector('.sketch-picker');
39
47
  const colorPanel = canvasElement.querySelector('.saturation-white');
@@ -47,12 +55,26 @@ export const Red: Story = {
47
55
  args: {
48
56
  value: { r: 220, g: 53, b: 69, a: 1 },
49
57
  },
58
+ parameters: {
59
+ docs: {
60
+ description: {
61
+ story: 'Renders the Color field with a red RGBA value pre-selected in the sketch picker.',
62
+ },
63
+ },
64
+ },
50
65
  async play({ canvasElement }) {
51
66
  await expect(canvasElement.querySelector('.sketch-picker')).toBeInTheDocument();
52
67
  },
53
68
  };
54
69
 
55
70
  export const Empty: Story = {
71
+ parameters: {
72
+ docs: {
73
+ description: {
74
+ story: 'Renders the Color field with no value — the sketch picker mounts empty.',
75
+ },
76
+ },
77
+ },
56
78
  async play({ canvasElement }) {
57
79
  await expect(canvasElement.querySelector('.sketch-picker')).toBeInTheDocument();
58
80
  },
@@ -63,6 +85,14 @@ export const Disabled: Story = {
63
85
  value: { r: 100, g: 149, b: 237, a: 1 },
64
86
  disabled: true,
65
87
  },
88
+ parameters: {
89
+ docs: {
90
+ description: {
91
+ story:
92
+ 'Renders the Color field with a value but disabled — the sketch picker is visible but non-interactive.',
93
+ },
94
+ },
95
+ },
66
96
  async play({ canvasElement }) {
67
97
  await expect(canvasElement.querySelector('.sketch-picker')).toBeInTheDocument();
68
98
  },
@@ -39,6 +39,14 @@ export const Default: Story = {
39
39
  'aria-label': label,
40
40
  })),
41
41
  },
42
+ parameters: {
43
+ docs: {
44
+ description: {
45
+ story:
46
+ 'Renders the Cron field with a "1 1 1 1 1" schedule split across the Minute / Hour / Day / Month / Weekday inputs. Typing into a segment fires onChange with the recomposed expression; the clear button resets the value.',
47
+ },
48
+ },
49
+ },
42
50
 
43
51
  async play({ args, canvasElement }) {
44
52
  const canvas = within(canvasElement);
@@ -30,6 +30,13 @@ export default meta;
30
30
  type Story = StoryObj<typeof meta>;
31
31
 
32
32
  export const Empty: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story: 'Renders the Date field with no value — the input mounts empty.',
37
+ },
38
+ },
39
+ },
33
40
  async play({ canvasElement }) {
34
41
  const input = canvasElement.querySelector('input');
35
42
  await expect(input).toBeInTheDocument();
@@ -40,6 +47,14 @@ export const WithValue: Story = {
40
47
  args: {
41
48
  value: '2025-01-15',
42
49
  },
50
+ parameters: {
51
+ docs: {
52
+ description: {
53
+ story:
54
+ 'Renders the Date field pre-populated with 2025-01-15 — the input shows the initial value without firing onChange.',
55
+ },
56
+ },
57
+ },
43
58
  async play({ canvasElement, args }) {
44
59
  const input = canvasElement.querySelector('input');
45
60
  await expect(input).toBeInTheDocument();
@@ -52,4 +67,11 @@ export const Disabled: Story = {
52
67
  value: '2025-06-01',
53
68
  disabled: true,
54
69
  },
70
+ parameters: {
71
+ docs: {
72
+ description: {
73
+ story: 'Renders the Date field with a value but disabled — the input is non-interactive.',
74
+ },
75
+ },
76
+ },
55
77
  };
@@ -30,6 +30,14 @@ export default meta;
30
30
  type Story = StoryObj<typeof meta>;
31
31
 
32
32
  export const Empty: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story:
37
+ 'Renders the File field with no value — the "Click or drop files here to upload" drop zone is visible.',
38
+ },
39
+ },
40
+ },
33
41
  async play({ canvasElement }) {
34
42
  const canvas = within(canvasElement);
35
43
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -44,6 +52,14 @@ export const WithValue: Story = {
44
52
  size: 28736,
45
53
  },
46
54
  },
55
+ parameters: {
56
+ docs: {
57
+ description: {
58
+ story:
59
+ 'Renders the File field pre-populated with a PDF value — the file name is displayed in place of the drop zone.',
60
+ },
61
+ },
62
+ },
47
63
  async play({ canvasElement }) {
48
64
  const canvas = within(canvasElement);
49
65
  await expect(canvas.getByText('report.pdf')).toBeInTheDocument();
@@ -58,6 +74,14 @@ export const WithImageValue: Story = {
58
74
  size: 1024,
59
75
  },
60
76
  },
77
+ parameters: {
78
+ docs: {
79
+ description: {
80
+ story:
81
+ 'Renders the File field pre-populated with a PNG image value — the file name and an inline preview thumbnail are shown.',
82
+ },
83
+ },
84
+ },
61
85
  async play({ canvasElement }) {
62
86
  const canvas = within(canvasElement);
63
87
  await expect(canvas.getByText('photo.png')).toBeInTheDocument();
@@ -75,6 +99,14 @@ export const WithAcceptedExtensions: Story = {
75
99
  },
76
100
  },
77
101
  },
102
+ parameters: {
103
+ docs: {
104
+ description: {
105
+ story:
106
+ 'Renders the File field with an accept option limiting uploads to PNG, JPEG and PDF — the allowed extensions are listed below the drop zone.',
107
+ },
108
+ },
109
+ },
78
110
  async play({ canvasElement }) {
79
111
  const canvas = within(canvasElement);
80
112
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -86,6 +118,14 @@ export const Multiple: Story = {
86
118
  args: {
87
119
  multiple: true,
88
120
  },
121
+ parameters: {
122
+ docs: {
123
+ description: {
124
+ story:
125
+ 'Renders the File field with multiple-selection enabled — the underlying input carries the multiple attribute and the drop zone stays visible after files are added.',
126
+ },
127
+ },
128
+ },
89
129
  async play({ canvasElement }) {
90
130
  const canvas = within(canvasElement);
91
131
  await expect(canvas.getByText('Click or drop files here to upload')).toBeInTheDocument();
@@ -105,6 +145,14 @@ export const WithBuildTab: Story = {
105
145
  content: { type: 'string', ui_type: 'string', display_name: 'Content', required: true },
106
146
  },
107
147
  },
148
+ parameters: {
149
+ docs: {
150
+ description: {
151
+ story:
152
+ 'Renders the File field with an argSchema — a "Build a File" tab appears alongside the "Upload a File" drop zone so the user can synthesize a file from schema fields.',
153
+ },
154
+ },
155
+ },
108
156
  async play({ canvasElement }) {
109
157
  const canvas = within(canvasElement);
110
158
  await expect(canvas.getByText('Upload a File')).toBeInTheDocument();
@@ -39,6 +39,14 @@ export const WithValue: Story = {
39
39
  args: {
40
40
  value: 'Hello world',
41
41
  },
42
+ parameters: {
43
+ docs: {
44
+ description: {
45
+ story:
46
+ 'Renders the LongString field pre-populated with "Hello world". Clicking clear empties the textarea and fires onChange; typing new content updates the value after the debounce.',
47
+ },
48
+ },
49
+ },
42
50
  async play({ canvasElement, args, step }) {
43
51
  const canvas = within(canvasElement);
44
52
  const textarea = canvas.getByLabelText('LongString');
@@ -66,6 +74,14 @@ export const WithValue: Story = {
66
74
  };
67
75
 
68
76
  export const Empty: Story = {
77
+ parameters: {
78
+ docs: {
79
+ description: {
80
+ story:
81
+ 'Renders the LongString field with no value. Typing into the empty textarea updates the value and fires onChange after the debounce.',
82
+ },
83
+ },
84
+ },
69
85
  async play({ canvasElement, args }) {
70
86
  const canvas = within(canvasElement);
71
87
  const textarea = canvas.getByLabelText('LongString');
@@ -86,6 +102,14 @@ export const Disabled: Story = {
86
102
  value: 'Cannot edit this',
87
103
  disabled: true,
88
104
  },
105
+ parameters: {
106
+ docs: {
107
+ description: {
108
+ story:
109
+ 'Renders the LongString field with a value but disabled — the textarea shows the value and rejects further edits.',
110
+ },
111
+ },
112
+ },
89
113
  async play({ canvasElement }) {
90
114
  const canvas = within(canvasElement);
91
115
  const textarea = canvas.getByLabelText('LongString');
@@ -35,6 +35,14 @@ export const Default: Story = {
35
35
  args: {
36
36
  'aria-label': 'MarkdownEditor',
37
37
  },
38
+ parameters: {
39
+ docs: {
40
+ description: {
41
+ story:
42
+ 'Renders the Markdown field pre-populated with the sample markdown fixture — the editor and the live preview render side by side.',
43
+ },
44
+ },
45
+ },
38
46
  async play({ canvasElement, args }) {
39
47
  const canvas = within(canvasElement);
40
48
  const editor = canvas.getByLabelText('MarkdownEditor');
@@ -2,6 +2,7 @@ import { ReqoreMultiSelect } from '@qoretechnologies/reqore';
2
2
  import { TReqoreMultiSelectItem } from '@qoretechnologies/reqore/dist/components/MultiSelect';
3
3
  import { IQorusAllowedValue } from '@qoretechnologies/ts-toolkit';
4
4
  import { memo, useMemo } from 'react';
5
+ import { getSelectItemShortDescription } from '../select/SelectCollection';
5
6
 
6
7
  export interface IMultiSelectFormFieldProps {
7
8
  /** Currently selected values (plain scalars) */
@@ -33,16 +34,19 @@ export const MultiSelectFormField = memo(
33
34
 
34
35
  const reqoreItems = useMemo<TReqoreMultiSelectItem[]>(() => {
35
36
  const base = items.map(
36
- (item): TReqoreMultiSelectItem => ({
37
- value: item.value?.value as string,
38
- label: item.display_name ?? String(item.value?.value ?? ''),
39
- description: item.short_desc || item.desc,
40
- tooltip: item.desc || item.short_desc,
41
- wrap: true,
42
- // IDE parity: when `*` is selected, every other item is disabled.
43
- disabled:
44
- !!item.disabled || (selectedValues.includes('*') && item.value?.value !== '*'),
45
- })
37
+ (item): TReqoreMultiSelectItem => {
38
+ const description = getSelectItemShortDescription(item);
39
+
40
+ return {
41
+ value: item.value?.value as string,
42
+ label: item.display_name ?? String(item.value?.value ?? ''),
43
+ description,
44
+ wrap: true,
45
+ // IDE parity: when `*` is selected, every other item is disabled.
46
+ disabled:
47
+ !!item.disabled || (selectedValues.includes('*') && item.value?.value !== '*'),
48
+ };
49
+ }
46
50
  );
47
51
 
48
52
  // IDE parity: selected values that aren't in the allowed list still
@@ -38,6 +38,14 @@ export const Integer: Story = {
38
38
  value: 42,
39
39
  type: 'int',
40
40
  },
41
+ parameters: {
42
+ docs: {
43
+ description: {
44
+ story:
45
+ 'Renders the Number field in integer mode with 42 pre-populated. Typing a new integer updates the value and fires onChange after the debounce.',
46
+ },
47
+ },
48
+ },
41
49
  async play({ args, canvasElement }) {
42
50
  const canvas = within(canvasElement);
43
51
  const input = canvas.getByLabelText('Number');
@@ -58,6 +66,14 @@ export const Float: Story = {
58
66
  value: 3.14,
59
67
  type: 'float',
60
68
  },
69
+ parameters: {
70
+ docs: {
71
+ description: {
72
+ story:
73
+ 'Renders the Number field in float mode with 3.14 pre-populated — the input uses a 0.1 step and accepts decimals.',
74
+ },
75
+ },
76
+ },
61
77
  async play({ args, canvasElement }) {
62
78
  const canvas = within(canvasElement);
63
79
  const input = canvas.getByLabelText('Number');
@@ -76,6 +92,13 @@ export const Empty: Story = {
76
92
  args: {
77
93
  type: 'int',
78
94
  },
95
+ parameters: {
96
+ docs: {
97
+ description: {
98
+ story: 'Renders the Number field in integer mode with no value — the input mounts empty.',
99
+ },
100
+ },
101
+ },
79
102
  async play({ canvasElement }) {
80
103
  const canvas = within(canvasElement);
81
104
  const input = canvas.getByLabelText('Number');
@@ -91,6 +114,14 @@ export const Disabled: Story = {
91
114
  type: 'int',
92
115
  disabled: true,
93
116
  },
117
+ parameters: {
118
+ docs: {
119
+ description: {
120
+ story:
121
+ 'Renders the Number field with a value but disabled — the input shows the value and rejects further edits.',
122
+ },
123
+ },
124
+ },
94
125
  async play({ canvasElement }) {
95
126
  const canvas = within(canvasElement);
96
127
  const input = canvas.getByLabelText('Number');