@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
@@ -27,6 +27,14 @@ export const Basic: Story = {
27
27
  args: {
28
28
  menu: typedMenu,
29
29
  },
30
+ parameters: {
31
+ docs: {
32
+ description: {
33
+ story:
34
+ 'Renders ReqraftMenu with the mock Qorus navigation — sections and items appear in the sidebar with no active path highlighted.',
35
+ },
36
+ },
37
+ },
30
38
  play: async () => {
31
39
  await testsWaitForText('Interfaces & More');
32
40
  },
@@ -36,6 +44,14 @@ export const ActivePath: Story = {
36
44
  path: '/Interfaces/mapper',
37
45
  menu: typedMenu,
38
46
  },
47
+ parameters: {
48
+ docs: {
49
+ description: {
50
+ story:
51
+ 'Renders ReqraftMenu with the /Interfaces/mapper path — the matching menu entry is highlighted as active.',
52
+ },
53
+ },
54
+ },
39
55
  play: async () => {
40
56
  await testsWaitForText('Interfaces & More');
41
57
  },
@@ -47,6 +63,14 @@ export const ActiveMenuItemIntent: Story = {
47
63
  menu: typedMenu,
48
64
  activeItemIntent: 'success',
49
65
  },
66
+ parameters: {
67
+ docs: {
68
+ description: {
69
+ story:
70
+ 'Renders ReqraftMenu with the /Interfaces/mapper path and a "success" activeItemIntent — the active entry uses the success intent for its highlight colour.',
71
+ },
72
+ },
73
+ },
50
74
  play: async () => {
51
75
  await testsWaitForText('Interfaces & More');
52
76
  },
@@ -57,6 +81,14 @@ export const WithDefaultQuery: Story = {
57
81
  menu: typedMenu,
58
82
  defaultQuery: 'mapper',
59
83
  },
84
+ parameters: {
85
+ docs: {
86
+ description: {
87
+ story:
88
+ 'Renders ReqraftMenu with a defaultQuery of "mapper" — the search input is pre-filled and the visible items are narrowed to those matching the query.',
89
+ },
90
+ },
91
+ },
60
92
  play: async () => {
61
93
  await testsWaitForText('Interfaces & More');
62
94
  await expect(document.querySelector('.reqore-input')).toHaveValue('mapper');
@@ -69,6 +101,14 @@ export const Filtered: Story = {
69
101
  menu: typedMenu,
70
102
  onQueryChange: fn(),
71
103
  },
104
+ parameters: {
105
+ docs: {
106
+ description: {
107
+ story:
108
+ 'Renders ReqraftMenu with an onQueryChange handler. Typing "step" into the search filter narrows the visible items to just the matching entries.',
109
+ },
110
+ },
111
+ },
72
112
  play: async () => {
73
113
  await testsWaitForText('Interfaces & More');
74
114
  await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'step' } });
@@ -82,6 +122,12 @@ export const Filtered: Story = {
82
122
  export const WidthFromStorage: Story = {
83
123
  ...ActivePath,
84
124
  parameters: {
125
+ docs: {
126
+ description: {
127
+ story:
128
+ 'Renders ReqraftMenu backed by user storage that already holds a persisted width — the menu mounts at the stored width rather than the default.',
129
+ },
130
+ },
85
131
  mockData: [...storiesStorageMock],
86
132
  },
87
133
  };
@@ -93,6 +139,14 @@ export const WithCustomChildren: Story = {
93
139
  topChildren: <div style={{ padding: 10 }}>Top Custom Child</div>,
94
140
  bottomChildren: <div style={{ padding: 10 }}>Bottom Custom Child</div>,
95
141
  },
142
+ parameters: {
143
+ docs: {
144
+ description: {
145
+ story:
146
+ 'Renders ReqraftMenu with topChildren and bottomChildren slots — custom nodes appear above and below the menu list.',
147
+ },
148
+ },
149
+ },
96
150
  play: async () => {
97
151
  await testsWaitForText('Interfaces & More');
98
152
  await testsWaitForText('Top Custom Child');
@@ -1,6 +1,6 @@
1
1
  import { StoryObj } from '@storybook/react-vite';
2
- import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
3
2
  import { useState } from 'react';
3
+ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
4
4
  import { sleep } from '../../../__tests__/utils';
5
5
  import { StoryMeta } from '../../types';
6
6
  import {
@@ -83,14 +83,16 @@ function mockTokenizeQonsole(text: string): number[] {
83
83
  for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
84
84
  const line = lines[lineIdx];
85
85
  // Strings first so they swallow any `--`/digits inside their quotes.
86
- const TOKEN_RE =
87
- /("(?:\\.|[^"\\])*")|(\d+(?:\.\d+)?)|(--[A-Za-z][\w-]*)|(\/[A-Za-z][\w-]*)/g;
86
+ const TOKEN_RE = /("(?:\\.|[^"\\])*")|(\d+(?:\.\d+)?)|(--[A-Za-z][\w-]*)|(\/[A-Za-z][\w-]*)/g;
88
87
  let match: RegExpExecArray | null;
89
88
  while ((match = TOKEN_RE.exec(line)) !== null) {
90
89
  let type = -1;
91
- if (match[1]) type = 11; // string
92
- else if (match[2]) type = 12; // number
93
- else if (match[3]) type = 9; // modifier — `--flag`
90
+ if (match[1])
91
+ type = 11; // string
92
+ else if (match[2])
93
+ type = 12; // number
94
+ else if (match[3])
95
+ type = 9; // modifier — `--flag`
94
96
  else if (match[4]) type = 8; // keyword — `/verb`
95
97
  if (type >= 0) {
96
98
  tokens.push({
@@ -102,7 +104,7 @@ function mockTokenizeQonsole(text: string): number[] {
102
104
  }
103
105
  }
104
106
  }
105
- tokens.sort((a, b) => (a.line - b.line) || (a.char - b.char));
107
+ tokens.sort((a, b) => a.line - b.line || a.char - b.char);
106
108
  // Delta-encode per LSP spec.
107
109
  const data: number[] = [];
108
110
  let prevLine = 0;
@@ -127,11 +129,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
127
129
  experimental: {
128
130
  qonsole: {
129
131
  version: '1.0',
130
- methods: [
131
- 'qonsole/assist',
132
- 'qonsole/validate',
133
- 'qonsole/setContext',
134
- ],
132
+ methods: ['qonsole/assist', 'qonsole/validate', 'qonsole/setContext'],
135
133
  },
136
134
  },
137
135
  },
@@ -213,8 +211,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
213
211
  short_desc: 'Guided setup for a new connection',
214
212
  verb: 'create',
215
213
  resource: 'connections',
216
- start_path:
217
- '/api/latest/qonsole/wizards/create-connection/start',
214
+ start_path: '/api/latest/qonsole/wizards/create-connection/start',
218
215
  },
219
216
  command: {
220
217
  title: 'Start Create connection wizard',
@@ -227,8 +224,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
227
224
  short_desc: 'Guided setup for a new connection',
228
225
  verb: 'create',
229
226
  resource: 'connections',
230
- start_path:
231
- '/api/latest/qonsole/wizards/create-connection/start',
227
+ start_path: '/api/latest/qonsole/wizards/create-connection/start',
232
228
  },
233
229
  ],
234
230
  },
@@ -237,10 +233,34 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
237
233
  ];
238
234
  } else if (ctx.type === 'resource') {
239
235
  items = [
240
- withTextEdit({ label: 'services', insertText: 'services', kind: 7, detail: 'Service interfaces', commitCharacters: [' '] }),
241
- withTextEdit({ label: 'workflows', insertText: 'workflows', kind: 7, detail: 'Workflow interfaces', commitCharacters: [' '] }),
242
- withTextEdit({ label: 'jobs', insertText: 'jobs', kind: 7, detail: 'Job interfaces', commitCharacters: [' '] }),
243
- withTextEdit({ label: 'users', insertText: 'users', kind: 7, detail: 'IDP users', commitCharacters: [' '] }),
236
+ withTextEdit({
237
+ label: 'services',
238
+ insertText: 'services',
239
+ kind: 7,
240
+ detail: 'Service interfaces',
241
+ commitCharacters: [' '],
242
+ }),
243
+ withTextEdit({
244
+ label: 'workflows',
245
+ insertText: 'workflows',
246
+ kind: 7,
247
+ detail: 'Workflow interfaces',
248
+ commitCharacters: [' '],
249
+ }),
250
+ withTextEdit({
251
+ label: 'jobs',
252
+ insertText: 'jobs',
253
+ kind: 7,
254
+ detail: 'Job interfaces',
255
+ commitCharacters: [' '],
256
+ }),
257
+ withTextEdit({
258
+ label: 'users',
259
+ insertText: 'users',
260
+ kind: 7,
261
+ detail: 'IDP users',
262
+ commitCharacters: [' '],
263
+ }),
244
264
  ];
245
265
  } else if (ctx.type === 'flag') {
246
266
  items = [
@@ -275,9 +295,24 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
275
295
  ];
276
296
  } else if (ctx.type === 'flag-value') {
277
297
  items = [
278
- withTextEdit({ label: 'qorus', insertText: 'qorus', kind: 12, detail: 'Qorus core app' }),
279
- withTextEdit({ label: 'qorus-ide', insertText: 'qorus-ide', kind: 12, detail: 'Qorus IDE' }),
280
- withTextEdit({ label: 'qorus-creator', insertText: 'qorus-creator', kind: 12, detail: 'Qorus Creator' }),
298
+ withTextEdit({
299
+ label: 'qorus',
300
+ insertText: 'qorus',
301
+ kind: 12,
302
+ detail: 'Qorus core app',
303
+ }),
304
+ withTextEdit({
305
+ label: 'qorus-ide',
306
+ insertText: 'qorus-ide',
307
+ kind: 12,
308
+ detail: 'Qorus IDE',
309
+ }),
310
+ withTextEdit({
311
+ label: 'qorus-creator',
312
+ insertText: 'qorus-creator',
313
+ kind: 12,
314
+ detail: 'Qorus Creator',
315
+ }),
281
316
  ];
282
317
  }
283
318
  }
@@ -363,6 +398,14 @@ export const BasicMock: Story = {
363
398
  args: {
364
399
  initialValue: '/list services ',
365
400
  },
401
+ parameters: {
402
+ docs: {
403
+ description: {
404
+ story:
405
+ 'Renders the QonsoleSmartInput against a mocked Qonsole LSP. Typing "-" after "/list services " opens the flag dropdown with --desc, --limit and --search; pressing Enter accepts the first item without producing a stray double dash.',
406
+ },
407
+ },
408
+ },
366
409
  async beforeEach() {
367
410
  const server = setupBasicQonsoleMockServer();
368
411
  return () => server.close();
@@ -474,6 +517,12 @@ export const LiveQonsoleWithContext: Story = {
474
517
  },
475
518
  parameters: {
476
519
  chromatic: { disable: true },
520
+ docs: {
521
+ description: {
522
+ story:
523
+ 'Renders the QonsoleSmartInput against the real Qonsole LSP with a pre-bound "/use services" context — predictive text is scoped to the services resource via the qonsole/setContext call.',
524
+ },
525
+ },
477
526
  },
478
527
  };
479
528
 
@@ -520,8 +569,7 @@ export const WithDiagnostics: Story = {
520
569
  start: { line: 0, character: 6 },
521
570
  end: { line: 0, character: 14 },
522
571
  },
523
- message:
524
- 'Unknown resource "services" — did you mean "service"?',
572
+ message: 'Unknown resource "services" — did you mean "service"?',
525
573
  severity: 1,
526
574
  },
527
575
  {
@@ -676,9 +724,9 @@ export const WithWizardItems: Story = {
676
724
  () => {
677
725
  const dropdown = document.querySelector('.reqore-menu');
678
726
  expect(dropdown).not.toBeNull();
679
- const item = Array.from(
680
- document.querySelectorAll('.reqore-menu-item')
681
- ).find((el) => el.textContent?.includes('Create connection'));
727
+ const item = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
728
+ el.textContent?.includes('Create connection')
729
+ );
682
730
  expect(item).toBeDefined();
683
731
  },
684
732
  { timeout: 30000 }
@@ -701,7 +749,6 @@ export const WithWizardItems: Story = {
701
749
  */
702
750
  export const SelectingWizardItemFiresCallback: Story = {
703
751
  args: {
704
- initialValue: '/',
705
752
  onWizardStart: fn(),
706
753
  },
707
754
  parameters: {
@@ -727,23 +774,22 @@ export const SelectingWizardItemFiresCallback: Story = {
727
774
  await userEvent.type(editable, '/');
728
775
  await waitFor(
729
776
  () => {
730
- const item = Array.from(
731
- document.querySelectorAll('.reqore-menu-item')
732
- ).find((el) => el.textContent?.includes('Create connection'));
777
+ const item = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
778
+ el.textContent?.includes('Create connection')
779
+ );
733
780
  expect(item).toBeDefined();
734
781
  },
735
782
  { timeout: 30000 }
736
783
  );
737
- const wizardItem = Array.from(
738
- document.querySelectorAll('.reqore-menu-item')
739
- ).find((el) => el.textContent?.includes('Create connection'));
784
+ const wizardItem = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
785
+ el.textContent?.includes('Create connection')
786
+ );
740
787
  await userEvent.click(wizardItem as HTMLElement);
741
788
  // The callback fired with the wizard descriptor...
742
789
  await waitFor(() => expect(args.onWizardStart).toHaveBeenCalled(), {
743
790
  timeout: 30000,
744
791
  });
745
- const callArg = (args.onWizardStart as ReturnType<typeof fn>).mock
746
- .calls[0][0];
792
+ const callArg = (args.onWizardStart as ReturnType<typeof fn>).mock.calls[0][0];
747
793
  expect(callArg).toMatchObject({ action: 'start-wizard' });
748
794
  // ...and NO text was inserted (wizard launch ≠ completion insert).
749
795
  expect(editable.textContent).toBe('/');
@@ -0,0 +1,131 @@
1
+ import { ReqoreControlGroup, ReqoreDropdown, ReqoreInput } from '@qoretechnologies/reqore';
2
+ import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
3
+ import styled, { css } from 'styled-components';
4
+
5
+ /*
6
+ * A search box with filter dropdowns beside it — the strip that sits above a list
7
+ * and narrows it.
8
+ *
9
+ * This existed twice, built independently either side of the package boundary: the
10
+ * ticket list in qorus-ide and the References tab here in reqraft. Same
11
+ * composition (ReqoreControlGroup > ReqoreInput + N ReqoreDropdowns), but they had
12
+ * drifted — one sized the group, the other sized each control; one wrapped, the
13
+ * other stacked; one moved the dropdown caret to the right slot and suppressed the
14
+ * default, the other did not. Two strips that should be indistinguishable ended up
15
+ * visibly different, and any fix had to be made twice.
16
+ *
17
+ * It lives in reqraft rather than Reqore because it is a composition of Reqore
18
+ * primitives, not a new primitive — there is nothing here Reqore is missing.
19
+ *
20
+ * The frosted container is part of the component, not the caller's job. Extracting
21
+ * only the controls left the two surfaces still looking different: one bar sat in a
22
+ * bordered, blurred card, the other bare on the page. If the bar is meant to read
23
+ * the same everywhere then its own chrome has to travel with it.
24
+ */
25
+
26
+ const Container = styled.div<{ $sticky?: boolean }>`
27
+ padding: 8px;
28
+ border-radius: 8px;
29
+ border: 1px solid rgba(255, 255, 255, 0.08);
30
+ background: rgba(255, 255, 255, 0.04);
31
+ backdrop-filter: blur(6px);
32
+
33
+ ${({ $sticky }) =>
34
+ $sticky &&
35
+ css`
36
+ position: sticky;
37
+ top: 0;
38
+ z-index: 2;
39
+ `}
40
+ `;
41
+
42
+ export interface ISearchFilterOption {
43
+ label: string;
44
+ icon?: IReqoreIconName;
45
+ selected?: boolean;
46
+ onClick: () => void;
47
+ }
48
+
49
+ export interface ISearchFilter {
50
+ /** Icon for the trigger — usually the selected option's. */
51
+ icon?: IReqoreIconName;
52
+ /** Trigger label — usually the selected option's. */
53
+ label: string;
54
+ items: ISearchFilterOption[];
55
+ }
56
+
57
+ export interface ISearchFilterBarProps {
58
+ value: string;
59
+ onChange: (value: string) => void;
60
+ placeholder: string;
61
+ filters?: ISearchFilter[];
62
+ /**
63
+ * Stack the search over its filters. On a phone the box and two dropdowns
64
+ * cannot share a row without the input collapsing to a few characters, so the
65
+ * caller passes its own breakpoint result rather than this guessing.
66
+ */
67
+ stacked?: boolean;
68
+ /**
69
+ * Pin the bar to the top of its scroll container. For a bar inside a scrolling
70
+ * panel (the References tab); a bar above a page-level list does not need it.
71
+ */
72
+ sticky?: boolean;
73
+ }
74
+
75
+ export const SearchFilterBar = ({
76
+ value,
77
+ onChange,
78
+ placeholder,
79
+ filters = [],
80
+ stacked,
81
+ sticky,
82
+ }: ISearchFilterBarProps) => (
83
+ <Container $sticky={sticky}>
84
+ {/* `size='small'` on the GROUP, not per control: the bar filters the content, it
85
+ is not content — at the default size the dropdowns read as primary actions and
86
+ crowd the search field. */}
87
+ <ReqoreControlGroup
88
+ fluid
89
+ wrap
90
+ gapSize='small'
91
+ size='small'
92
+ vertical={stacked}
93
+ verticalAlign={stacked ? undefined : 'center'}
94
+ >
95
+ <ReqoreInput
96
+ icon='Search2Line'
97
+ placeholder={placeholder}
98
+ value={value}
99
+ onChange={(event) => onChange((event.currentTarget as HTMLInputElement).value)}
100
+ onClearClick={value ? () => onChange('') : undefined}
101
+ minimal
102
+ fluid
103
+ />
104
+ {filters.length ?
105
+ <ReqoreControlGroup gapSize='small' size='small' fluid={stacked}>
106
+ {filters.map((filter, index) => (
107
+ <ReqoreDropdown
108
+ key={index}
109
+ icon={filter.icon}
110
+ label={filter.label}
111
+ minimal
112
+ flat
113
+ /* stretch to share the row when stacked; hug the label otherwise */
114
+ fixed={!stacked}
115
+ /* the caret defaults to the LEFT slot, where the filter's own icon
116
+ already sits — without this each trigger shows two glyphs */
117
+ rightIcon='ArrowDownSLine'
118
+ showCaret={false}
119
+ items={filter.items.map((item) => ({
120
+ label: item.label,
121
+ icon: item.icon,
122
+ selected: item.selected,
123
+ onClick: item.onClick,
124
+ }))}
125
+ />
126
+ ))}
127
+ </ReqoreControlGroup>
128
+ : null}
129
+ </ReqoreControlGroup>
130
+ </Container>
131
+ );
@@ -0,0 +1 @@
1
+ export * from './SearchFilterBar';
@@ -72,6 +72,14 @@ export const BasicMock: Story = {
72
72
  initialValue: '',
73
73
  triggerCharacters: new Set([' ', '.']),
74
74
  },
75
+ parameters: {
76
+ docs: {
77
+ description: {
78
+ story:
79
+ 'Renders the SmartEditor against a mocked LSP server. Typing the "." trigger character sends a completion request and the dropdown mounts with the apple and banana suggestions.',
80
+ },
81
+ },
82
+ },
75
83
  async beforeEach() {
76
84
  const lsp = createMockLspServer(MOCK_LSP_URL, {
77
85
  capabilities: {},
@@ -295,6 +303,14 @@ export const WithDiagnostics: Story = {
295
303
  languageId: 'demo',
296
304
  initialValue: 'list services in pricing',
297
305
  },
306
+ parameters: {
307
+ docs: {
308
+ description: {
309
+ story:
310
+ 'Renders the SmartEditor against a mocked LSP server that pushes diagnostics on didOpen — the text is decorated with wavy underlines and the stacked message panel below the editor lists both diagnostics.',
311
+ },
312
+ },
313
+ },
298
314
  async beforeEach() {
299
315
  const lsp = createMockLspServer(MOCK_LSP_URL, {
300
316
  capabilities: {},
@@ -342,6 +358,14 @@ export const ReadOnly: Story = {
342
358
  initialValue: 'frozen content',
343
359
  readOnly: true,
344
360
  },
361
+ parameters: {
362
+ docs: {
363
+ description: {
364
+ story:
365
+ 'Renders the SmartEditor in read-only mode over an initial value — the Slate editor mounts as contenteditable=false so the content shows but cannot be edited.',
366
+ },
367
+ },
368
+ },
345
369
  async beforeEach() {
346
370
  const lsp = createMockLspServer(MOCK_LSP_URL, { capabilities: {} });
347
371
  return () => lsp.close();
@@ -367,6 +391,14 @@ export const LoadingOverlay: Story = {
367
391
  languageId: 'demo',
368
392
  initialValue: 'visible under the overlay',
369
393
  },
394
+ parameters: {
395
+ docs: {
396
+ description: {
397
+ story:
398
+ 'Renders the SmartEditor against a mock LSP that never finishes initialize — the "Connecting to language server" overlay stays over the editor for the whole session.',
399
+ },
400
+ },
401
+ },
370
402
  async beforeEach() {
371
403
  const lsp = createMockLspServer(MOCK_LSP_URL, {
372
404
  capabilities: {},
@@ -0,0 +1,171 @@
1
+ import { StoryObj } from '@storybook/react-vite';
2
+ import { useState } from 'react';
3
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
4
+ import { StoryMeta } from '../../types';
5
+ import { AttachmentChips } from './AttachmentChips';
6
+
7
+ /**
8
+ * Staged-file chips for a ticket composer. Image files show a thumbnail and open the shared
9
+ * `ImageLightbox` on click; other files show a paperclip. The pencil renames a file inline
10
+ * (a File name is read-only, so the host swaps in a renamed copy); the ✕ removes it.
11
+ */
12
+ const meta = {
13
+ component: AttachmentChips,
14
+ title: 'Components/AttachmentChips',
15
+ } as StoryMeta<typeof AttachmentChips>;
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ const imageFile = (label: string, hue: number, name: string): File => {
20
+ const svg =
21
+ `<svg xmlns='http://www.w3.org/2000/svg' width='640' height='400'>` +
22
+ `<rect width='640' height='400' fill='hsl(${hue},40%,16%)'/>` +
23
+ `<text x='24' y='210' fill='white' font-family='sans-serif' font-size='30'>${label}</text></svg>`;
24
+ return new File([svg], name, { type: 'image/svg+xml' });
25
+ };
26
+ const textFile = (name: string): File => new File(['log contents'], name, { type: 'text/plain' });
27
+
28
+ const FILES = [
29
+ imageFile('before', 265, 'screenshot-2026-07-26-17-41-44.png'),
30
+ imageFile('after', 205, 'canvas-after.png'),
31
+ textFile('server.log'),
32
+ ];
33
+
34
+ // Plays the composer's role: owns the file set, swaps in a renamed copy on rename.
35
+ const Harness = () => {
36
+ const [files, setFiles] = useState<File[]>(FILES);
37
+ return (
38
+ <AttachmentChips
39
+ files={files}
40
+ onRemove={(index) => setFiles((current) => current.filter((_, j) => j !== index))}
41
+ onRename={(index, name) =>
42
+ setFiles((current) =>
43
+ current.map((file, i) => (i === index ? new File([file], name, { type: file.type }) : file))
44
+ )
45
+ }
46
+ />
47
+ );
48
+ };
49
+
50
+ /**
51
+ * Two screenshots + a log. Clicking a screenshot chip opens the lightbox (paging across the two
52
+ * images); the log stays a plain chip. Proves the preview opens and paints the image.
53
+ */
54
+ export const WithImages: Story = {
55
+ parameters: {
56
+ docs: {
57
+ description: {
58
+ story:
59
+ 'Renders staged chips for two screenshots and a log, where clicking a screenshot chip opens the shared lightbox while the log stays a plain chip.',
60
+ },
61
+ },
62
+ },
63
+ render: () => <Harness />,
64
+ play: async ({ canvasElement }) => {
65
+ const canvas = within(canvasElement);
66
+ await expect(canvas.getByText('screenshot-2026-07-26-17-41-44.png')).toBeInTheDocument();
67
+ await expect(canvas.getByText('server.log')).toBeInTheDocument();
68
+
69
+ // clicking an image chip opens the shared lightbox (portals to the body)
70
+ await userEvent.click(canvas.getByText('canvas-after.png'));
71
+ await waitFor(() => expect(document.querySelector('[role="dialog"]')).not.toBeNull());
72
+ await waitFor(() =>
73
+ expect(document.body.querySelector('img[alt="canvas-after.png"]')).not.toBeNull()
74
+ );
75
+ },
76
+ };
77
+
78
+ /**
79
+ * Renaming from INSIDE the preview: open a chip's lightbox, use its pencil, and the new
80
+ * name lands on the chip too — the lightbox funnels into the same host `onRename` as the
81
+ * chip pencil, so both affordances stay one mechanism.
82
+ */
83
+ export const RenameFromPreview: Story = {
84
+ parameters: {
85
+ docs: {
86
+ description: {
87
+ story:
88
+ 'Renders the staged chips and renames a screenshot from inside its lightbox — the preview’s pencil commits the new name, and the chip outside carries it too.',
89
+ },
90
+ },
91
+ },
92
+ render: () => <Harness />,
93
+ play: async ({ canvasElement }) => {
94
+ const canvas = within(canvasElement);
95
+ await userEvent.click(canvas.getByText('canvas-after.png'));
96
+ await waitFor(() => expect(document.querySelector('[role="dialog"]')).not.toBeNull());
97
+
98
+ const pencil = await waitFor(() => {
99
+ const el = document.body.querySelector<HTMLButtonElement>('.imagelightbox-rename');
100
+ if (!el) {
101
+ throw new Error('lightbox rename action not rendered');
102
+ }
103
+ return el;
104
+ });
105
+ await userEvent.click(pencil);
106
+ const input = await waitFor(() => {
107
+ const el = document.body.querySelector<HTMLInputElement>('.reqore-modal input');
108
+ if (!el) {
109
+ throw new Error('lightbox rename input not shown');
110
+ }
111
+ return el;
112
+ });
113
+ await userEvent.clear(input);
114
+ await userEvent.type(input, 'after-fix.png');
115
+ await userEvent.keyboard('{Enter}');
116
+
117
+ // commit ends the edit; a second Esc now closes the modal itself
118
+ await userEvent.keyboard('{Escape}');
119
+ await waitFor(() => expect(document.querySelector('[role="dialog"]')).toBeNull());
120
+
121
+ // the rename reached the host: the CHIP carries the new name
122
+ await waitFor(() => expect(canvas.getByText('after-fix.png')).toBeInTheDocument());
123
+ await expect(canvas.queryByText('canvas-after.png')).not.toBeInTheDocument();
124
+ },
125
+ };
126
+
127
+ /**
128
+ * Renaming an auto-named screenshot: the pencil turns the chip into an inline input; Enter
129
+ * commits and the chip shows the new name. Discriminating — remove the rename wiring and the
130
+ * chip keeps its `screenshot-<timestamp>.png` name.
131
+ */
132
+ export const Rename: Story = {
133
+ parameters: {
134
+ docs: {
135
+ description: {
136
+ story:
137
+ 'Renders the staged chips and renames an auto-named screenshot inline — the pencil opens an input, Enter commits, and the chip shows the new name.',
138
+ },
139
+ },
140
+ },
141
+ render: () => <Harness />,
142
+ play: async ({ canvasElement }) => {
143
+ const canvas = within(canvasElement);
144
+ const chip = canvas.getByText('screenshot-2026-07-26-17-41-44.png').closest('.reqore-tag');
145
+ if (!chip) {
146
+ throw new Error('chip not found');
147
+ }
148
+ // click the rename pencil (its own action; the ✕ is a separate reqore-tag-remove)
149
+ const renameAction = chip.querySelector<HTMLElement>('.reqore-tag-action:not(.reqore-tag-remove)');
150
+ if (!renameAction) {
151
+ throw new Error('rename action not rendered');
152
+ }
153
+ await userEvent.click(renameAction);
154
+
155
+ // the inline input appears, pre-filled with the current name
156
+ const input = await waitFor(() => {
157
+ const el = canvasElement.querySelector<HTMLInputElement>('input');
158
+ if (!el) {
159
+ throw new Error('rename input not shown');
160
+ }
161
+ return el;
162
+ });
163
+ await userEvent.clear(input);
164
+ await userEvent.type(input, 'mapper-error.png');
165
+ await userEvent.keyboard('{Enter}');
166
+
167
+ // the chip now carries the new name; the auto-name is gone
168
+ await waitFor(() => expect(canvas.getByText('mapper-error.png')).toBeInTheDocument());
169
+ await expect(canvas.queryByText('screenshot-2026-07-26-17-41-44.png')).not.toBeInTheDocument();
170
+ },
171
+ };