@nyaruka/temba-components 0.131.1 → 0.131.3

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 (485) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +75 -1
  3. package/demo/components/floating-tabs/example.html +400 -0
  4. package/demo/components/flow/index.html +1 -1
  5. package/demo/data/flows/food-order.json +2 -2
  6. package/demo/data/flows/sample-flow.json +113 -125
  7. package/demo/data/flows/voicemail.json +613 -0
  8. package/demo/index.html +6 -0
  9. package/dist/locales/es.js +5 -5
  10. package/dist/locales/es.js.map +1 -1
  11. package/dist/locales/fr.js +5 -5
  12. package/dist/locales/fr.js.map +1 -1
  13. package/dist/locales/locale-codes.js +11 -2
  14. package/dist/locales/locale-codes.js.map +1 -1
  15. package/dist/locales/pt.js +5 -5
  16. package/dist/locales/pt.js.map +1 -1
  17. package/dist/static/svg/index.svg +1 -1
  18. package/dist/temba-components.js +1773 -662
  19. package/dist/temba-components.js.map +1 -1
  20. package/out-tsc/src/Icons.js +4 -1
  21. package/out-tsc/src/Icons.js.map +1 -1
  22. package/out-tsc/src/display/FloatingTab.js +167 -0
  23. package/out-tsc/src/display/FloatingTab.js.map +1 -0
  24. package/out-tsc/src/display/ProgressBar.js +22 -2
  25. package/out-tsc/src/display/ProgressBar.js.map +1 -1
  26. package/out-tsc/src/events.js.map +1 -1
  27. package/out-tsc/src/flow/CanvasMenu.js +200 -0
  28. package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
  29. package/out-tsc/src/flow/CanvasNode.js +489 -47
  30. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  31. package/out-tsc/src/flow/Editor.js +1417 -67
  32. package/out-tsc/src/flow/Editor.js.map +1 -1
  33. package/out-tsc/src/flow/NodeEditor.js +479 -112
  34. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  35. package/out-tsc/src/flow/NodeTypeSelector.js +540 -0
  36. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
  37. package/out-tsc/src/flow/StickyNote.js +12 -3
  38. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  39. package/out-tsc/src/flow/actions/add_contact_groups.js +4 -3
  40. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  41. package/out-tsc/src/flow/actions/add_contact_urn.js +63 -4
  42. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  43. package/out-tsc/src/flow/actions/add_input_labels.js +4 -3
  44. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  45. package/out-tsc/src/flow/actions/play_audio.js +3 -2
  46. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  47. package/out-tsc/src/flow/actions/remove_contact_groups.js +7 -5
  48. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  49. package/out-tsc/src/flow/actions/request_optin.js +3 -2
  50. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  51. package/out-tsc/src/flow/actions/say_msg.js +3 -2
  52. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  53. package/out-tsc/src/flow/actions/send_broadcast.js +77 -23
  54. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  55. package/out-tsc/src/flow/actions/send_email.js +5 -5
  56. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  57. package/out-tsc/src/flow/actions/send_msg.js +101 -21
  58. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  59. package/out-tsc/src/flow/actions/set_contact_channel.js +6 -9
  60. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  61. package/out-tsc/src/flow/actions/set_contact_field.js +20 -20
  62. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  63. package/out-tsc/src/flow/actions/set_contact_language.js +3 -2
  64. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  65. package/out-tsc/src/flow/actions/set_contact_name.js +3 -12
  66. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  67. package/out-tsc/src/flow/actions/set_contact_status.js +3 -2
  68. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  69. package/out-tsc/src/flow/actions/set_run_result.js +4 -3
  70. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  71. package/out-tsc/src/flow/actions/start_session.js +181 -6
  72. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  73. package/out-tsc/src/flow/config.js +11 -23
  74. package/out-tsc/src/flow/config.js.map +1 -1
  75. package/out-tsc/src/flow/currencies.js +45 -0
  76. package/out-tsc/src/flow/currencies.js.map +1 -0
  77. package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
  78. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
  79. package/out-tsc/src/flow/nodes/shared.js +71 -0
  80. package/out-tsc/src/flow/nodes/shared.js.map +1 -0
  81. package/out-tsc/src/flow/nodes/split_by_airtime.js +211 -5
  82. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  83. package/out-tsc/src/flow/nodes/split_by_contact_field.js +152 -3
  84. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  85. package/out-tsc/src/flow/nodes/split_by_expression.js +73 -2
  86. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  87. package/out-tsc/src/flow/nodes/split_by_groups.js +18 -10
  88. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  89. package/out-tsc/src/flow/nodes/split_by_intent.js +8 -0
  90. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
  91. package/out-tsc/src/flow/nodes/split_by_llm.js +11 -3
  92. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  93. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +10 -3
  94. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  95. package/out-tsc/src/flow/nodes/split_by_random.js +10 -4
  96. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  97. package/out-tsc/src/flow/nodes/split_by_resthook.js +113 -0
  98. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
  99. package/out-tsc/src/flow/nodes/split_by_run_result.js +211 -3
  100. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  101. package/out-tsc/src/flow/nodes/split_by_scheme.js +158 -2
  102. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  103. package/out-tsc/src/flow/nodes/split_by_subflow.js +13 -5
  104. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  105. package/out-tsc/src/flow/nodes/split_by_ticket.js +10 -3
  106. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  107. package/out-tsc/src/flow/nodes/split_by_webhook.js +10 -3
  108. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  109. package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
  110. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  111. package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
  112. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  113. package/out-tsc/src/flow/nodes/wait_for_response.js +38 -568
  114. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  115. package/out-tsc/src/flow/types.js +86 -12
  116. package/out-tsc/src/flow/types.js.map +1 -1
  117. package/out-tsc/src/flow/utils.js +101 -14
  118. package/out-tsc/src/flow/utils.js.map +1 -1
  119. package/out-tsc/src/form/FieldRenderer.js +2 -4
  120. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  121. package/out-tsc/src/interfaces.js +3 -0
  122. package/out-tsc/src/interfaces.js.map +1 -1
  123. package/out-tsc/src/layout/FloatingWindow.js +346 -0
  124. package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
  125. package/out-tsc/src/list/SortableList.js +98 -33
  126. package/out-tsc/src/list/SortableList.js.map +1 -1
  127. package/out-tsc/src/live/ContactChat.js +6 -25
  128. package/out-tsc/src/live/ContactChat.js.map +1 -1
  129. package/out-tsc/src/locales/es.js +5 -5
  130. package/out-tsc/src/locales/es.js.map +1 -1
  131. package/out-tsc/src/locales/fr.js +5 -5
  132. package/out-tsc/src/locales/fr.js.map +1 -1
  133. package/out-tsc/src/locales/locale-codes.js +11 -2
  134. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  135. package/out-tsc/src/locales/pt.js +5 -5
  136. package/out-tsc/src/locales/pt.js.map +1 -1
  137. package/out-tsc/src/store/AppState.js +120 -0
  138. package/out-tsc/src/store/AppState.js.map +1 -1
  139. package/out-tsc/src/utils.js +254 -13
  140. package/out-tsc/src/utils.js.map +1 -1
  141. package/out-tsc/temba-modules.js +8 -0
  142. package/out-tsc/temba-modules.js.map +1 -1
  143. package/out-tsc/test/ActionHelper.js +3 -3
  144. package/out-tsc/test/ActionHelper.js.map +1 -1
  145. package/out-tsc/test/NodeHelper.js +6 -3
  146. package/out-tsc/test/NodeHelper.js.map +1 -1
  147. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  148. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  149. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  150. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  151. package/out-tsc/test/actions/send_email.test.js +17 -23
  152. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  153. package/out-tsc/test/actions/send_msg.test.js +33 -15
  154. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  155. package/out-tsc/test/actions/start_session.test.js +116 -0
  156. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  157. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  158. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  159. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  160. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  161. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  162. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  163. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  164. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  165. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  166. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  167. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  168. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  169. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  170. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  171. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  172. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  173. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  174. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  175. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  176. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  177. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  178. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  179. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  180. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  181. package/out-tsc/test/temba-floating-tab.test.js +91 -0
  182. package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
  183. package/out-tsc/test/temba-floating-window.test.js +301 -0
  184. package/out-tsc/test/temba-floating-window.test.js.map +1 -0
  185. package/out-tsc/test/temba-flow-editor-node.test.js +202 -2
  186. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  187. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  188. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  189. package/out-tsc/test/temba-localization.test.js +471 -0
  190. package/out-tsc/test/temba-localization.test.js.map +1 -0
  191. package/out-tsc/test/temba-node-editor.test.js +3 -1
  192. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  193. package/out-tsc/test/temba-node-type-selector.test.js +265 -0
  194. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  195. package/out-tsc/test/temba-omnibox.test.js +2 -1
  196. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  197. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  198. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  199. package/out-tsc/test/temba-utils-index.test.js +1 -27
  200. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  201. package/out-tsc/test/utils.test.js +20 -0
  202. package/out-tsc/test/utils.test.js.map +1 -1
  203. package/package.json +2 -1
  204. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  205. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  206. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  207. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  208. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  209. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  210. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  211. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  212. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  213. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  214. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  215. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  216. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  217. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  218. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  219. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  220. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  221. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  222. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  223. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  224. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  225. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  226. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  227. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  228. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  229. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  230. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  231. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  232. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  233. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  234. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  235. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  236. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  237. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  238. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  239. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  240. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  241. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  242. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  243. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  244. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  245. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  246. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  247. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  248. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  249. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  250. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  251. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  252. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  253. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  254. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  255. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  256. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  257. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  258. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  259. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  260. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  261. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  262. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  263. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  264. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  265. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  266. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  267. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  268. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  269. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  270. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  271. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  272. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  273. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  274. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  275. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  276. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  277. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  278. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  279. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  280. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  281. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  282. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  283. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  284. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  285. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  286. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  287. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  288. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  289. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  290. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  291. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  292. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  293. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  294. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  295. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  296. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  297. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  298. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  299. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  300. package/screenshots/truth/canvas-menu/open.png +0 -0
  301. package/screenshots/truth/editor/router.png +0 -0
  302. package/screenshots/truth/editor/wait.png +0 -0
  303. package/screenshots/truth/floating-tab/default.png +0 -0
  304. package/screenshots/truth/floating-tab/gray.png +0 -0
  305. package/screenshots/truth/floating-tab/green.png +0 -0
  306. package/screenshots/truth/floating-tab/hidden.png +0 -0
  307. package/screenshots/truth/floating-tab/hover.png +0 -0
  308. package/screenshots/truth/floating-tab/purple.png +0 -0
  309. package/screenshots/truth/floating-window/chromeless.png +0 -0
  310. package/screenshots/truth/floating-window/custom-size.png +0 -0
  311. package/screenshots/truth/floating-window/default.png +0 -0
  312. package/screenshots/truth/floating-window/with-header.png +0 -0
  313. package/screenshots/truth/list/fields-dragging.png +0 -0
  314. package/screenshots/truth/list/sortable-dragging.png +0 -0
  315. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  316. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  317. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  318. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  319. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  320. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  321. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  322. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  323. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  324. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  325. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  326. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  327. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  328. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  329. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  330. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  331. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  332. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  333. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  334. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  335. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  336. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  337. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  338. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  339. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  340. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  341. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  342. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  343. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  344. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  345. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  346. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  347. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  348. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  349. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  350. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  351. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  352. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  353. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  354. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  355. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  356. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  357. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  358. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  359. package/src/Icons.ts +4 -1
  360. package/src/display/FloatingTab.ts +174 -0
  361. package/src/display/ProgressBar.ts +22 -2
  362. package/src/events.ts +2 -8
  363. package/src/flow/CanvasMenu.ts +217 -0
  364. package/src/flow/CanvasNode.ts +596 -40
  365. package/src/flow/Editor.ts +1721 -45
  366. package/src/flow/NodeEditor.ts +621 -144
  367. package/src/flow/NodeTypeSelector.ts +636 -0
  368. package/src/flow/StickyNote.ts +12 -3
  369. package/src/flow/actions/add_contact_groups.ts +5 -4
  370. package/src/flow/actions/add_contact_urn.ts +78 -4
  371. package/src/flow/actions/add_input_labels.ts +5 -4
  372. package/src/flow/actions/play_audio.ts +3 -2
  373. package/src/flow/actions/remove_contact_groups.ts +16 -6
  374. package/src/flow/actions/request_optin.ts +3 -2
  375. package/src/flow/actions/say_msg.ts +3 -2
  376. package/src/flow/actions/send_broadcast.ts +86 -23
  377. package/src/flow/actions/send_email.ts +12 -6
  378. package/src/flow/actions/send_msg.ts +155 -34
  379. package/src/flow/actions/set_contact_channel.ts +6 -11
  380. package/src/flow/actions/set_contact_field.ts +21 -25
  381. package/src/flow/actions/set_contact_language.ts +11 -4
  382. package/src/flow/actions/set_contact_name.ts +4 -15
  383. package/src/flow/actions/set_contact_status.ts +4 -3
  384. package/src/flow/actions/set_run_result.ts +5 -4
  385. package/src/flow/actions/start_session.ts +210 -6
  386. package/src/flow/config.ts +11 -23
  387. package/src/flow/currencies.ts +51 -0
  388. package/src/flow/nodes/shared-rules.ts +301 -0
  389. package/src/flow/nodes/shared.ts +87 -0
  390. package/src/flow/nodes/split_by_airtime.ts +255 -5
  391. package/src/flow/nodes/split_by_contact_field.ts +195 -3
  392. package/src/flow/nodes/split_by_expression.ts +104 -2
  393. package/src/flow/nodes/split_by_groups.ts +26 -11
  394. package/src/flow/nodes/split_by_intent.ts +8 -0
  395. package/src/flow/nodes/split_by_llm.ts +22 -4
  396. package/src/flow/nodes/split_by_llm_categorize.ts +22 -5
  397. package/src/flow/nodes/split_by_random.ts +16 -6
  398. package/src/flow/nodes/split_by_resthook.ts +140 -0
  399. package/src/flow/nodes/split_by_run_result.ts +259 -3
  400. package/src/flow/nodes/split_by_scheme.ts +202 -2
  401. package/src/flow/nodes/split_by_subflow.ts +17 -5
  402. package/src/flow/nodes/split_by_ticket.ts +15 -4
  403. package/src/flow/nodes/split_by_webhook.ts +17 -6
  404. package/src/flow/nodes/wait_for_digits.ts +3 -2
  405. package/src/flow/nodes/wait_for_menu.ts +3 -2
  406. package/src/flow/nodes/wait_for_response.ts +59 -680
  407. package/src/flow/types.ts +156 -23
  408. package/src/flow/utils.ts +108 -14
  409. package/src/form/FieldRenderer.ts +2 -4
  410. package/src/interfaces.ts +3 -0
  411. package/src/layout/FloatingWindow.ts +386 -0
  412. package/src/list/SortableList.ts +109 -34
  413. package/src/live/ContactChat.ts +7 -25
  414. package/src/locales/es.ts +18 -13
  415. package/src/locales/fr.ts +18 -13
  416. package/src/locales/locale-codes.ts +11 -2
  417. package/src/locales/pt.ts +18 -13
  418. package/src/store/AppState.ts +173 -0
  419. package/src/store/flow-definition.d.ts +2 -5
  420. package/src/utils.ts +332 -12
  421. package/static/api/channels.json +46 -0
  422. package/static/api/llms.json +18 -0
  423. package/static/api/resthooks.json +31 -0
  424. package/static/svg/index.svg +1 -1
  425. package/static/svg/work/traced/lightning-02.svg +1 -0
  426. package/static/svg/work/used/lightning-02.svg +3 -0
  427. package/temba-modules.ts +8 -0
  428. package/test/ActionHelper.ts +3 -3
  429. package/test/NodeHelper.ts +6 -3
  430. package/test/actions/add_contact_urn.test.ts +287 -0
  431. package/test/actions/send_broadcast.test.ts +190 -0
  432. package/test/actions/send_email.test.ts +17 -23
  433. package/test/actions/send_msg.test.ts +39 -15
  434. package/test/actions/start_session.test.ts +151 -0
  435. package/test/nodes/split_by_airtime.test.ts +673 -0
  436. package/test/nodes/split_by_contact_field.test.ts +451 -0
  437. package/test/nodes/split_by_expression.test.ts +751 -0
  438. package/test/nodes/split_by_random.test.ts +3 -3
  439. package/test/nodes/split_by_resthook.test.ts +398 -0
  440. package/test/nodes/split_by_run_result.test.ts +1109 -0
  441. package/test/nodes/split_by_scheme.test.ts +486 -0
  442. package/test/nodes/split_by_subflow.test.ts +381 -0
  443. package/test/nodes/wait_for_digits.test.ts +2 -2
  444. package/test/nodes/wait_for_response.test.ts +2 -1
  445. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  446. package/test/temba-canvas-menu.test.ts +156 -0
  447. package/test/temba-floating-tab.test.ts +110 -0
  448. package/test/temba-floating-window.test.ts +477 -0
  449. package/test/temba-flow-editor-node.test.ts +246 -2
  450. package/test/temba-flow-editor.test.ts +7 -8
  451. package/test/temba-localization.test.ts +611 -0
  452. package/test/temba-node-editor.test.ts +3 -1
  453. package/test/temba-node-type-selector.test.ts +355 -0
  454. package/test/temba-omnibox.test.ts +2 -1
  455. package/test/temba-sortable-list.test.ts +69 -0
  456. package/test/temba-utils-index.test.ts +0 -35
  457. package/test/utils.test.ts +22 -0
  458. package/test-assets/contacts/history.json +14 -21
  459. package/test-assets/select/llms.json +2 -2
  460. package/web-dev-server.config.mjs +49 -1
  461. package/web-test-runner.config.mjs +0 -1
  462. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  463. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  464. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  465. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  466. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  467. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  468. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  469. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  470. package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
  471. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
  472. package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
  473. package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
  474. package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
  475. package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
  476. package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
  477. package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
  478. package/src/flow/actions/call_classifier.ts +0 -12
  479. package/src/flow/actions/call_resthook.ts +0 -12
  480. package/src/flow/actions/split_by_expression_example.ts +0 -88
  481. package/src/flow/actions/transfer_airtime.ts +0 -12
  482. package/src/flow/nodes/wait_for_audio.ts +0 -7
  483. package/src/flow/nodes/wait_for_image.ts +0 -7
  484. package/src/flow/nodes/wait_for_location.ts +0 -7
  485. package/src/flow/nodes/wait_for_video.ts +0 -7
@@ -0,0 +1,301 @@
1
+ import { expect, assert } from '@open-wc/testing';
2
+ import { FloatingWindow } from '../src/layout/FloatingWindow';
3
+ import { assertScreenshot, getComponent } from './utils.test';
4
+ describe('temba-floating-window', () => {
5
+ it('can be created', async () => {
6
+ const window = (await getComponent('temba-floating-window', {
7
+ header: 'Phone Simulator',
8
+ width: 250,
9
+ maxHeight: 700,
10
+ top: 100
11
+ }, '<div style="padding: 20px;">Window content goes here</div>', 300, 750));
12
+ assert.instanceOf(window, FloatingWindow);
13
+ expect(window.header).to.equal('Phone Simulator');
14
+ expect(window.width).to.equal(250);
15
+ expect(window.maxHeight).to.equal(700);
16
+ expect(window.top).to.equal(100);
17
+ // show the window for screenshot
18
+ window.hidden = false;
19
+ await window.updateComplete;
20
+ expect(window.hidden).to.equal(false);
21
+ // use custom clip for fixed positioned element
22
+ const windowElement = window.shadowRoot.querySelector('.window');
23
+ const clip = {
24
+ x: window.left,
25
+ y: window.top,
26
+ width: window.width,
27
+ height: windowElement.offsetHeight
28
+ };
29
+ await assertScreenshot('floating-window/default', clip);
30
+ });
31
+ it('starts hidden by default', async () => {
32
+ const window = (await getComponent('temba-floating-window', {
33
+ header: 'Test Window'
34
+ }, '<div>Content</div>'));
35
+ expect(window.hidden).to.equal(true);
36
+ expect(window.classList.contains('hidden')).to.equal(true);
37
+ });
38
+ it('can be shown and hidden', async () => {
39
+ const window = (await getComponent('temba-floating-window', {
40
+ header: 'Test Window',
41
+ hidden: true
42
+ }, '<div>Content</div>'));
43
+ expect(window.hidden).to.equal(true);
44
+ window.show();
45
+ await window.updateComplete;
46
+ expect(window.hidden).to.equal(false);
47
+ expect(window.classList.contains('hidden')).to.equal(false);
48
+ window.hide();
49
+ await window.updateComplete;
50
+ expect(window.hidden).to.equal(true);
51
+ expect(window.classList.contains('hidden')).to.equal(true);
52
+ });
53
+ it('fires close event when close button clicked', async () => {
54
+ const window = (await getComponent('temba-floating-window', {
55
+ header: 'Test Window'
56
+ }, '<div>Content</div>', 300, 750));
57
+ // show the window first
58
+ window.hidden = false;
59
+ await window.updateComplete;
60
+ let closed = false;
61
+ window.addEventListener('temba-dialog-hidden', () => {
62
+ closed = true;
63
+ });
64
+ const closeButton = window.shadowRoot.querySelector('.close-button');
65
+ expect(closeButton).to.exist;
66
+ closeButton.click();
67
+ await window.updateComplete;
68
+ expect(closed).to.equal(true);
69
+ expect(window.hidden).to.equal(true);
70
+ });
71
+ it('displays header correctly', async () => {
72
+ const window = (await getComponent('temba-floating-window', {
73
+ header: 'Phone Simulator'
74
+ }, '<div>Content</div>', 300, 400));
75
+ window.hidden = false;
76
+ await window.updateComplete;
77
+ const titleElement = window.shadowRoot.querySelector('.title');
78
+ expect(titleElement).to.exist;
79
+ expect(titleElement.textContent).to.equal('Phone Simulator');
80
+ // use custom clip for fixed positioned element
81
+ const windowElement = window.shadowRoot.querySelector('.window');
82
+ const clip = {
83
+ x: window.left,
84
+ y: window.top,
85
+ width: window.width,
86
+ height: windowElement.offsetHeight
87
+ };
88
+ await assertScreenshot('floating-window/with-header', clip);
89
+ });
90
+ it('renders slot content', async () => {
91
+ const window = (await getComponent('temba-floating-window', {
92
+ header: 'Test'
93
+ }, '<div class="test-content">Custom content</div>', 300, 400));
94
+ window.hidden = false;
95
+ await window.updateComplete;
96
+ const slotContent = window.querySelector('.test-content');
97
+ expect(slotContent).to.exist;
98
+ expect(slotContent.textContent).to.equal('Custom content');
99
+ });
100
+ it('supports custom dimensions', async () => {
101
+ const window = (await getComponent('temba-floating-window', {
102
+ header: 'Custom Size',
103
+ width: 400,
104
+ maxHeight: 600,
105
+ top: 100,
106
+ left: 100
107
+ }, '<div>Content</div>', 450, 650));
108
+ window.show();
109
+ await window.updateComplete;
110
+ expect(window.width).to.equal(400);
111
+ expect(window.maxHeight).to.equal(600);
112
+ expect(window.top).to.equal(100);
113
+ expect(window.left).to.equal(100);
114
+ // use custom clip for fixed positioned element
115
+ const windowElement = window.shadowRoot.querySelector('.window');
116
+ const clip = {
117
+ x: window.left,
118
+ y: window.top,
119
+ width: window.width,
120
+ height: windowElement.offsetHeight
121
+ };
122
+ await assertScreenshot('floating-window/custom-size', clip);
123
+ });
124
+ it('can be dragged by header', async () => {
125
+ const window = (await getComponent('temba-floating-window', {
126
+ header: 'Draggable Window',
127
+ width: 250,
128
+ maxHeight: 400,
129
+ top: 100,
130
+ left: 100
131
+ }, '<div>Content</div>', 300, 450));
132
+ window.hidden = false;
133
+ await window.updateComplete;
134
+ const header = window.shadowRoot.querySelector('.header');
135
+ expect(header).to.exist;
136
+ // simulate drag by setting dragging state
137
+ window.dragging = true;
138
+ await window.updateComplete;
139
+ const windowElement = window.shadowRoot.querySelector('.window');
140
+ expect(windowElement.classList.contains('dragging')).to.equal(true);
141
+ });
142
+ it('respects viewport bounds when dragging', async () => {
143
+ const window = (await getComponent('temba-floating-window', {
144
+ header: 'Bounded Window',
145
+ width: 250,
146
+ maxHeight: 400,
147
+ top: 100,
148
+ left: 100
149
+ }, '<div style="height: 200px;">Content with specific height</div>', 300, 450));
150
+ window.hidden = false;
151
+ await window.updateComplete;
152
+ // get actual window height
153
+ const windowElement = window.shadowRoot.querySelector('.window');
154
+ const actualHeight = windowElement.offsetHeight;
155
+ // simulate dragging near bottom of viewport
156
+ const viewportHeight = window.ownerDocument.defaultView.innerHeight;
157
+ const maxAllowedTop = viewportHeight - actualHeight;
158
+ // try to drag below the viewport
159
+ window.top = viewportHeight + 100;
160
+ await window.updateComplete;
161
+ // the handleMouseMove should clamp this, but we'll test the logic exists
162
+ expect(actualHeight).to.be.greaterThan(0);
163
+ expect(maxAllowedTop).to.be.lessThan(viewportHeight);
164
+ });
165
+ it('maintains consistent starting position', async () => {
166
+ const window = (await getComponent('temba-floating-window', {
167
+ header: 'Test',
168
+ width: 250,
169
+ maxHeight: 400,
170
+ top: 100,
171
+ left: 100
172
+ }, '<div>Content</div>', 300, 450));
173
+ window.hidden = false;
174
+ await window.updateComplete;
175
+ // verify initial position matches properties
176
+ expect(window.top).to.equal(100);
177
+ expect(window.left).to.equal(100);
178
+ // change position (simulating drag)
179
+ window.top = 200;
180
+ window.left = 200;
181
+ await window.updateComplete;
182
+ // hide and show
183
+ window.hide();
184
+ await window.updateComplete;
185
+ window.show();
186
+ await window.updateComplete;
187
+ // position should remain at property values (100, 100) not dragged position
188
+ expect(window.top).to.equal(100);
189
+ expect(window.left).to.equal(100);
190
+ });
191
+ it('can disable chrome', async () => {
192
+ const window = (await getComponent('temba-floating-window', {
193
+ header: 'Test',
194
+ width: 250,
195
+ maxHeight: 400,
196
+ top: 100,
197
+ left: 100,
198
+ chromeless: true
199
+ }, '<div style="background: white; padding: 20px;">Chromeless content</div>', 300, 450));
200
+ expect(window.chromeless).to.equal(true);
201
+ window.hidden = false;
202
+ await window.updateComplete;
203
+ const windowElement = window.shadowRoot.querySelector('.window');
204
+ expect(windowElement.classList.contains('chromeless')).to.equal(true);
205
+ // header should not be rendered
206
+ const header = window.shadowRoot.querySelector('.header');
207
+ expect(header).to.not.exist;
208
+ // body should have no padding
209
+ const body = window.shadowRoot.querySelector('.body');
210
+ const bodyStyles = getComputedStyle(body);
211
+ expect(bodyStyles.padding).to.equal('0px');
212
+ // use custom clip for fixed positioned element
213
+ const clip = {
214
+ x: window.left,
215
+ y: window.top,
216
+ width: window.width,
217
+ height: windowElement.offsetHeight
218
+ };
219
+ await assertScreenshot('floating-window/chromeless', clip);
220
+ });
221
+ it('defaults to showing chrome', async () => {
222
+ const window = (await getComponent('temba-floating-window', {
223
+ header: 'Test'
224
+ }, '<div>Content</div>'));
225
+ expect(window.chromeless).to.equal(false);
226
+ });
227
+ it('can close via public close() method', async () => {
228
+ const window = (await getComponent('temba-floating-window', {
229
+ header: 'Test',
230
+ chromeless: true
231
+ }, '<div>Content</div>'));
232
+ window.hidden = false;
233
+ await window.updateComplete;
234
+ expect(window.hidden).to.equal(false);
235
+ let eventFired = false;
236
+ window.addEventListener('temba-dialog-hidden', () => {
237
+ eventFired = true;
238
+ });
239
+ // call public close() method
240
+ window.close();
241
+ await window.updateComplete;
242
+ expect(window.hidden).to.equal(true);
243
+ expect(eventFired).to.equal(true);
244
+ });
245
+ it('chromeless window has no borders or shadows', async () => {
246
+ const window = (await getComponent('temba-floating-window', {
247
+ header: 'Test',
248
+ width: 250,
249
+ maxHeight: 400,
250
+ chromeless: true
251
+ }, '<div>Content</div>', 300, 450));
252
+ window.hidden = false;
253
+ await window.updateComplete;
254
+ const windowElement = window.shadowRoot.querySelector('.window');
255
+ const styles = getComputedStyle(windowElement);
256
+ expect(styles.boxShadow).to.equal('none');
257
+ expect(styles.borderRadius).to.equal('0px');
258
+ expect(styles.background.includes('rgba(0, 0, 0, 0)')).to.be.true;
259
+ });
260
+ it('supports min and max height constraints', async () => {
261
+ const window = (await getComponent('temba-floating-window', {
262
+ header: 'Min/Max Test',
263
+ width: 300,
264
+ minHeight: 200,
265
+ maxHeight: 500
266
+ }, '<div style="padding: 20px;">Content that can vary in height</div>', 350, 550));
267
+ window.hidden = false;
268
+ await window.updateComplete;
269
+ expect(window.minHeight).to.equal(200);
270
+ expect(window.maxHeight).to.equal(500);
271
+ // verify the styles are applied
272
+ const windowElement = window.shadowRoot.querySelector('.window');
273
+ const styles = getComputedStyle(windowElement);
274
+ expect(styles.minHeight).to.equal('200px');
275
+ expect(styles.maxHeight).to.equal('500px');
276
+ });
277
+ it('stays on screen when browser is resized', async () => {
278
+ const window = (await getComponent('temba-floating-window', {
279
+ header: 'Resize Test',
280
+ width: 250,
281
+ maxHeight: 400,
282
+ top: 100,
283
+ left: 100
284
+ }, '<div style="height: 200px;">Content</div>', 300, 450));
285
+ window.hidden = false;
286
+ await window.updateComplete;
287
+ // position window near right edge
288
+ const originalViewportWidth = window.ownerDocument.defaultView.innerWidth;
289
+ window.left = originalViewportWidth - window.width - 30;
290
+ await window.updateComplete;
291
+ // simulate window resize event (the component should constrain position)
292
+ window.dispatchEvent(new Event('resize', { bubbles: true }));
293
+ await window.updateComplete;
294
+ // window should still be within viewport bounds with 20px padding
295
+ const padding = 20;
296
+ expect(window.left).to.be.at.least(padding);
297
+ expect(window.left).to.be.at.most(window.ownerDocument.defaultView.innerWidth - window.width - padding);
298
+ expect(window.top).to.be.at.least(padding);
299
+ });
300
+ });
301
+ //# sourceMappingURL=temba-floating-window.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-floating-window.test.js","sourceRoot":"","sources":["../../test/temba-floating-window.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, assert } from '@open-wc/testing';\nimport { FloatingWindow } from '../src/layout/FloatingWindow';\nimport { assertScreenshot, getComponent } from './utils.test';\n\ndescribe('temba-floating-window', () => {\n it('can be created', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Phone Simulator',\n width: 250,\n maxHeight: 700,\n top: 100\n },\n '<div style=\"padding: 20px;\">Window content goes here</div>',\n 300,\n 750\n )) as FloatingWindow;\n\n assert.instanceOf(window, FloatingWindow);\n expect(window.header).to.equal('Phone Simulator');\n expect(window.width).to.equal(250);\n expect(window.maxHeight).to.equal(700);\n expect(window.top).to.equal(100);\n\n // show the window for screenshot\n window.hidden = false;\n await window.updateComplete;\n expect(window.hidden).to.equal(false);\n\n // use custom clip for fixed positioned element\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const clip = {\n x: window.left,\n y: window.top,\n width: window.width,\n height: windowElement.offsetHeight\n };\n await assertScreenshot('floating-window/default', clip);\n });\n\n it('starts hidden by default', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test Window'\n },\n '<div>Content</div>'\n )) as FloatingWindow;\n\n expect(window.hidden).to.equal(true);\n expect(window.classList.contains('hidden')).to.equal(true);\n });\n\n it('can be shown and hidden', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test Window',\n hidden: true\n },\n '<div>Content</div>'\n )) as FloatingWindow;\n\n expect(window.hidden).to.equal(true);\n\n window.show();\n await window.updateComplete;\n expect(window.hidden).to.equal(false);\n expect(window.classList.contains('hidden')).to.equal(false);\n\n window.hide();\n await window.updateComplete;\n expect(window.hidden).to.equal(true);\n expect(window.classList.contains('hidden')).to.equal(true);\n });\n\n it('fires close event when close button clicked', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test Window'\n },\n '<div>Content</div>',\n 300,\n 750\n )) as FloatingWindow;\n\n // show the window first\n window.hidden = false;\n await window.updateComplete;\n\n let closed = false;\n window.addEventListener('temba-dialog-hidden', () => {\n closed = true;\n });\n\n const closeButton = window.shadowRoot.querySelector(\n '.close-button'\n ) as HTMLElement;\n expect(closeButton).to.exist;\n\n closeButton.click();\n await window.updateComplete;\n\n expect(closed).to.equal(true);\n expect(window.hidden).to.equal(true);\n });\n\n it('displays header correctly', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Phone Simulator'\n },\n '<div>Content</div>',\n 300,\n 400\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n const titleElement = window.shadowRoot.querySelector('.title');\n expect(titleElement).to.exist;\n expect(titleElement.textContent).to.equal('Phone Simulator');\n\n // use custom clip for fixed positioned element\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const clip = {\n x: window.left,\n y: window.top,\n width: window.width,\n height: windowElement.offsetHeight\n };\n await assertScreenshot('floating-window/with-header', clip);\n });\n\n it('renders slot content', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test'\n },\n '<div class=\"test-content\">Custom content</div>',\n 300,\n 400\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n const slotContent = window.querySelector('.test-content');\n expect(slotContent).to.exist;\n expect(slotContent.textContent).to.equal('Custom content');\n });\n\n it('supports custom dimensions', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Custom Size',\n width: 400,\n maxHeight: 600,\n top: 100,\n left: 100\n },\n '<div>Content</div>',\n 450,\n 650\n )) as FloatingWindow;\n\n window.show();\n await window.updateComplete;\n expect(window.width).to.equal(400);\n expect(window.maxHeight).to.equal(600);\n expect(window.top).to.equal(100);\n expect(window.left).to.equal(100);\n\n // use custom clip for fixed positioned element\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const clip = {\n x: window.left,\n y: window.top,\n width: window.width,\n height: windowElement.offsetHeight\n };\n\n await assertScreenshot('floating-window/custom-size', clip);\n });\n\n it('can be dragged by header', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Draggable Window',\n width: 250,\n maxHeight: 400,\n top: 100,\n left: 100\n },\n '<div>Content</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n const header = window.shadowRoot.querySelector('.header') as HTMLElement;\n expect(header).to.exist;\n\n // simulate drag by setting dragging state\n window.dragging = true;\n await window.updateComplete;\n\n const windowElement = window.shadowRoot.querySelector('.window');\n expect(windowElement.classList.contains('dragging')).to.equal(true);\n });\n\n it('respects viewport bounds when dragging', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Bounded Window',\n width: 250,\n maxHeight: 400,\n top: 100,\n left: 100\n },\n '<div style=\"height: 200px;\">Content with specific height</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n // get actual window height\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const actualHeight = windowElement.offsetHeight;\n\n // simulate dragging near bottom of viewport\n const viewportHeight = window.ownerDocument.defaultView.innerHeight;\n const maxAllowedTop = viewportHeight - actualHeight;\n\n // try to drag below the viewport\n window.top = viewportHeight + 100;\n await window.updateComplete;\n\n // the handleMouseMove should clamp this, but we'll test the logic exists\n expect(actualHeight).to.be.greaterThan(0);\n expect(maxAllowedTop).to.be.lessThan(viewportHeight);\n });\n\n it('maintains consistent starting position', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test',\n width: 250,\n maxHeight: 400,\n top: 100,\n left: 100\n },\n '<div>Content</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n // verify initial position matches properties\n expect(window.top).to.equal(100);\n expect(window.left).to.equal(100);\n\n // change position (simulating drag)\n window.top = 200;\n window.left = 200;\n await window.updateComplete;\n\n // hide and show\n window.hide();\n await window.updateComplete;\n window.show();\n await window.updateComplete;\n\n // position should remain at property values (100, 100) not dragged position\n expect(window.top).to.equal(100);\n expect(window.left).to.equal(100);\n });\n\n it('can disable chrome', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test',\n width: 250,\n maxHeight: 400,\n top: 100,\n left: 100,\n chromeless: true\n },\n '<div style=\"background: white; padding: 20px;\">Chromeless content</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n expect(window.chromeless).to.equal(true);\n\n window.hidden = false;\n await window.updateComplete;\n\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n expect(windowElement.classList.contains('chromeless')).to.equal(true);\n\n // header should not be rendered\n const header = window.shadowRoot.querySelector('.header');\n expect(header).to.not.exist;\n\n // body should have no padding\n const body = window.shadowRoot.querySelector('.body') as HTMLElement;\n const bodyStyles = getComputedStyle(body);\n expect(bodyStyles.padding).to.equal('0px');\n\n // use custom clip for fixed positioned element\n const clip = {\n x: window.left,\n y: window.top,\n width: window.width,\n height: windowElement.offsetHeight\n };\n await assertScreenshot('floating-window/chromeless', clip);\n });\n\n it('defaults to showing chrome', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test'\n },\n '<div>Content</div>'\n )) as FloatingWindow;\n\n expect(window.chromeless).to.equal(false);\n });\n\n it('can close via public close() method', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test',\n chromeless: true\n },\n '<div>Content</div>'\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n expect(window.hidden).to.equal(false);\n\n let eventFired = false;\n window.addEventListener('temba-dialog-hidden', () => {\n eventFired = true;\n });\n\n // call public close() method\n window.close();\n await window.updateComplete;\n\n expect(window.hidden).to.equal(true);\n expect(eventFired).to.equal(true);\n });\n\n it('chromeless window has no borders or shadows', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Test',\n width: 250,\n maxHeight: 400,\n chromeless: true\n },\n '<div>Content</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const styles = getComputedStyle(windowElement);\n\n expect(styles.boxShadow).to.equal('none');\n expect(styles.borderRadius).to.equal('0px');\n expect(styles.background.includes('rgba(0, 0, 0, 0)')).to.be.true;\n });\n\n it('supports min and max height constraints', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Min/Max Test',\n width: 300,\n minHeight: 200,\n maxHeight: 500\n },\n '<div style=\"padding: 20px;\">Content that can vary in height</div>',\n 350,\n 550\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n expect(window.minHeight).to.equal(200);\n expect(window.maxHeight).to.equal(500);\n\n // verify the styles are applied\n const windowElement = window.shadowRoot.querySelector(\n '.window'\n ) as HTMLElement;\n const styles = getComputedStyle(windowElement);\n expect(styles.minHeight).to.equal('200px');\n expect(styles.maxHeight).to.equal('500px');\n });\n\n it('stays on screen when browser is resized', async () => {\n const window = (await getComponent(\n 'temba-floating-window',\n {\n header: 'Resize Test',\n width: 250,\n maxHeight: 400,\n top: 100,\n left: 100\n },\n '<div style=\"height: 200px;\">Content</div>',\n 300,\n 450\n )) as FloatingWindow;\n\n window.hidden = false;\n await window.updateComplete;\n\n // position window near right edge\n const originalViewportWidth = window.ownerDocument.defaultView.innerWidth;\n window.left = originalViewportWidth - window.width - 30;\n await window.updateComplete;\n\n // simulate window resize event (the component should constrain position)\n window.dispatchEvent(new Event('resize', { bubbles: true }));\n await window.updateComplete;\n\n // window should still be within viewport bounds with 20px padding\n const padding = 20;\n expect(window.left).to.be.at.least(padding);\n expect(window.left).to.be.at.most(\n window.ownerDocument.defaultView.innerWidth - window.width - padding\n );\n expect(window.top).to.be.at.least(padding);\n });\n});\n"]}
@@ -3,6 +3,7 @@ import { html, fixture, expect } from '@open-wc/testing';
3
3
  import { CanvasNode } from '../src/flow/CanvasNode';
4
4
  import { stub, restore, useFakeTimers } from 'sinon';
5
5
  import { CustomEventType } from '../src/interfaces';
6
+ import { ACTION_GROUPS } from '../src/flow/types';
6
7
  describe('EditorNode', () => {
7
8
  let editorNode;
8
9
  let mockPlumber;
@@ -177,7 +178,7 @@ describe('EditorNode', () => {
177
178
  var _a;
178
179
  const config = {
179
180
  name: 'Test Action',
180
- color: '#ff0000'
181
+ group: ACTION_GROUPS.send // Uses 'send' group which has color '#3498db'
181
182
  };
182
183
  const mockAction = {
183
184
  type: 'send_msg',
@@ -189,7 +190,8 @@ describe('EditorNode', () => {
189
190
  expect(title).to.exist;
190
191
  const nameElement = title === null || title === void 0 ? void 0 : title.querySelector('.name');
191
192
  expect((_a = nameElement === null || nameElement === void 0 ? void 0 : nameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()).to.equal('Test Action');
192
- expect(title === null || title === void 0 ? void 0 : title.getAttribute('style')).to.contain('background:#ff0000');
193
+ // The 'send' group has color '#3498db' from ACTION_GROUP_METADATA
194
+ expect(title === null || title === void 0 ? void 0 : title.getAttribute('style')).to.contain('background:#3498db');
193
195
  });
194
196
  });
195
197
  describe('updated lifecycle', () => {
@@ -931,5 +933,203 @@ describe('EditorNode', () => {
931
933
  // This sequence ensures JSPlumb visuals stay in sync with the flow definition
932
934
  });
933
935
  });
936
+ describe('add action button', () => {
937
+ beforeEach(async () => {
938
+ editorNode = new CanvasNode();
939
+ editorNode['plumber'] = mockPlumber;
940
+ });
941
+ it('handleAddActionClick fires AddActionRequested event', () => {
942
+ const mockNode = {
943
+ uuid: 'test-node',
944
+ actions: [
945
+ {
946
+ type: 'send_msg',
947
+ uuid: 'action-1',
948
+ text: 'Hello',
949
+ quick_replies: []
950
+ }
951
+ ],
952
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
953
+ };
954
+ editorNode['node'] = mockNode;
955
+ let eventFired = false;
956
+ let eventDetail = null;
957
+ editorNode.addEventListener(CustomEventType.AddActionRequested, (e) => {
958
+ eventFired = true;
959
+ eventDetail = e.detail;
960
+ });
961
+ const mockEvent = {
962
+ preventDefault: stub(),
963
+ stopPropagation: stub()
964
+ };
965
+ // Call the add action click handler
966
+ editorNode.handleAddActionClick(mockEvent);
967
+ // Verify the event was fired with correct detail
968
+ expect(eventFired).to.be.true;
969
+ expect(eventDetail).to.exist;
970
+ expect(eventDetail.nodeUuid).to.equal('test-node');
971
+ // Verify event handlers were called
972
+ expect(mockEvent.preventDefault).to.have.been.called;
973
+ expect(mockEvent.stopPropagation).to.have.been.called;
974
+ });
975
+ it('renders add action button for execute_actions nodes', () => {
976
+ const mockNode = {
977
+ uuid: 'test-node',
978
+ actions: [
979
+ {
980
+ type: 'send_msg',
981
+ uuid: 'action-1',
982
+ text: 'Hello',
983
+ quick_replies: []
984
+ }
985
+ ],
986
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
987
+ };
988
+ const mockUI = {
989
+ position: { left: 0, top: 0 },
990
+ type: 'execute_actions'
991
+ };
992
+ editorNode['node'] = mockNode;
993
+ editorNode['ui'] = mockUI;
994
+ const rendered = editorNode.render();
995
+ expect(rendered).to.exist;
996
+ });
997
+ it('renders correctly for non-execute_actions nodes', () => {
998
+ const mockNode = {
999
+ uuid: 'test-node',
1000
+ actions: [],
1001
+ exits: [{ uuid: 'exit-1', destination_uuid: null }],
1002
+ router: {
1003
+ type: 'switch',
1004
+ categories: []
1005
+ }
1006
+ };
1007
+ const mockUI = {
1008
+ position: { left: 0, top: 0 },
1009
+ type: 'wait_for_response'
1010
+ };
1011
+ editorNode['node'] = mockNode;
1012
+ editorNode['ui'] = mockUI;
1013
+ const rendered = editorNode.render();
1014
+ expect(rendered).to.exist;
1015
+ });
1016
+ });
1017
+ describe('router section graying', () => {
1018
+ it('grays out split nodes when categories not included in translation', async () => {
1019
+ const mockNode = {
1020
+ uuid: 'split-node-1',
1021
+ actions: [],
1022
+ exits: [
1023
+ { uuid: 'exit-1', destination_uuid: null },
1024
+ { uuid: 'exit-2', destination_uuid: null }
1025
+ ],
1026
+ router: {
1027
+ type: 'switch',
1028
+ result_name: 'Response',
1029
+ categories: [
1030
+ { uuid: 'cat-1', name: 'Category 1', exit_uuid: 'exit-1' },
1031
+ { uuid: 'cat-2', name: 'Category 2', exit_uuid: 'exit-2' }
1032
+ ]
1033
+ }
1034
+ };
1035
+ const mockUI = {
1036
+ position: { left: 0, top: 0 },
1037
+ type: 'split_by_groups'
1038
+ };
1039
+ // Create node
1040
+ const editorNode = await fixture(html `<temba-flow-node
1041
+ .node=${mockNode}
1042
+ .ui=${mockUI}
1043
+ ></temba-flow-node>`);
1044
+ // Set fromStore properties directly
1045
+ editorNode.isTranslating = true;
1046
+ editorNode.includeCategoriesInTranslation = false;
1047
+ editorNode.languageCode = 'spa';
1048
+ // Force re-render
1049
+ editorNode.requestUpdate('isTranslating');
1050
+ editorNode.requestUpdate('includeCategoriesInTranslation');
1051
+ await editorNode.updateComplete;
1052
+ // Find the node element
1053
+ const nodeElement = editorNode.querySelector('.node');
1054
+ expect(nodeElement).to.exist;
1055
+ // Verify it has the non-localizable class
1056
+ expect(nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.classList.contains('non-localizable')).to.be.true;
1057
+ });
1058
+ it('does not gray out split nodes when categories are included in translation', async () => {
1059
+ const mockNode = {
1060
+ uuid: 'split-node-2',
1061
+ actions: [],
1062
+ exits: [
1063
+ { uuid: 'exit-1', destination_uuid: null },
1064
+ { uuid: 'exit-2', destination_uuid: null }
1065
+ ],
1066
+ router: {
1067
+ type: 'switch',
1068
+ result_name: 'Response',
1069
+ categories: [
1070
+ { uuid: 'cat-1', name: 'Category 1', exit_uuid: 'exit-1' },
1071
+ { uuid: 'cat-2', name: 'Category 2', exit_uuid: 'exit-2' }
1072
+ ]
1073
+ }
1074
+ };
1075
+ const mockUI = {
1076
+ position: { left: 0, top: 0 },
1077
+ type: 'split_by_groups'
1078
+ };
1079
+ // Create node
1080
+ const editorNode = await fixture(html `<temba-flow-node
1081
+ .node=${mockNode}
1082
+ .ui=${mockUI}
1083
+ ></temba-flow-node>`);
1084
+ // Set fromStore properties directly
1085
+ editorNode.isTranslating = true;
1086
+ editorNode.includeCategoriesInTranslation = true;
1087
+ editorNode.languageCode = 'spa';
1088
+ await editorNode.requestUpdate();
1089
+ await editorNode.updateComplete;
1090
+ // Find the node element
1091
+ const nodeElement = editorNode.querySelector('.node');
1092
+ expect(nodeElement).to.exist;
1093
+ // Verify it does NOT have the non-localizable class
1094
+ expect(nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.classList.contains('non-localizable')).to.be.false;
1095
+ });
1096
+ it('does not gray out split nodes when not translating', async () => {
1097
+ const mockNode = {
1098
+ uuid: 'split-node-3',
1099
+ actions: [],
1100
+ exits: [
1101
+ { uuid: 'exit-1', destination_uuid: null },
1102
+ { uuid: 'exit-2', destination_uuid: null }
1103
+ ],
1104
+ router: {
1105
+ type: 'switch',
1106
+ result_name: 'Response',
1107
+ categories: [
1108
+ { uuid: 'cat-1', name: 'Category 1', exit_uuid: 'exit-1' },
1109
+ { uuid: 'cat-2', name: 'Category 2', exit_uuid: 'exit-2' }
1110
+ ]
1111
+ }
1112
+ };
1113
+ const mockUI = {
1114
+ position: { left: 0, top: 0 },
1115
+ type: 'split_by_groups'
1116
+ };
1117
+ // Create node
1118
+ const editorNode = await fixture(html `<temba-flow-node
1119
+ .node=${mockNode}
1120
+ .ui=${mockUI}
1121
+ ></temba-flow-node>`);
1122
+ // Set fromStore properties directly
1123
+ editorNode.isTranslating = false;
1124
+ editorNode.languageCode = 'eng';
1125
+ await editorNode.requestUpdate();
1126
+ await editorNode.updateComplete;
1127
+ // Find the node element
1128
+ const nodeElement = editorNode.querySelector('.node');
1129
+ expect(nodeElement).to.exist;
1130
+ // Verify it does NOT have the non-localizable class
1131
+ expect(nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.classList.contains('non-localizable')).to.be.false;
1132
+ });
1133
+ });
934
1134
  });
935
1135
  //# sourceMappingURL=temba-flow-editor-node.test.js.map