@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.
- package/.github/workflows/publish.yml +4 -1
- package/CHANGELOG.md +75 -1
- package/demo/components/floating-tabs/example.html +400 -0
- package/demo/components/flow/index.html +1 -1
- package/demo/data/flows/food-order.json +2 -2
- package/demo/data/flows/sample-flow.json +113 -125
- package/demo/data/flows/voicemail.json +613 -0
- package/demo/index.html +6 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +11 -2
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1773 -662
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/Icons.js +4 -1
- package/out-tsc/src/Icons.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +167 -0
- package/out-tsc/src/display/FloatingTab.js.map +1 -0
- package/out-tsc/src/display/ProgressBar.js +22 -2
- package/out-tsc/src/display/ProgressBar.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasMenu.js +200 -0
- package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
- package/out-tsc/src/flow/CanvasNode.js +489 -47
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +1417 -67
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +479 -112
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +540 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
- package/out-tsc/src/flow/StickyNote.js +12 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +4 -3
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +63 -4
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +4 -3
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +3 -2
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +7 -5
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +3 -2
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +3 -2
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +77 -23
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +5 -5
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +101 -21
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +6 -9
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +20 -20
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +3 -2
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +3 -12
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +3 -2
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +4 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +181 -6
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -23
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/currencies.js +45 -0
- package/out-tsc/src/flow/currencies.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
- package/out-tsc/src/flow/nodes/shared.js +71 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +211 -5
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +152 -3
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +73 -2
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +18 -10
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +8 -0
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js +11 -3
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +10 -4
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +113 -0
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_run_result.js +211 -3
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_scheme.js +158 -2
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +13 -5
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +10 -3
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +38 -568
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +86 -12
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +101 -14
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +2 -4
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +3 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +346 -0
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
- package/out-tsc/src/list/SortableList.js +98 -33
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +6 -25
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +11 -2
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +120 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/src/utils.js +254 -13
- package/out-tsc/src/utils.js.map +1 -1
- package/out-tsc/temba-modules.js +8 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/ActionHelper.js +3 -3
- package/out-tsc/test/ActionHelper.js.map +1 -1
- package/out-tsc/test/NodeHelper.js +6 -3
- package/out-tsc/test/NodeHelper.js.map +1 -1
- package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
- package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
- package/out-tsc/test/actions/send_broadcast.test.js +148 -0
- package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
- package/out-tsc/test/actions/send_email.test.js +17 -23
- package/out-tsc/test/actions/send_email.test.js.map +1 -1
- package/out-tsc/test/actions/send_msg.test.js +33 -15
- package/out-tsc/test/actions/send_msg.test.js.map +1 -1
- package/out-tsc/test/actions/start_session.test.js +116 -0
- package/out-tsc/test/actions/start_session.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
- package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
- package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
- package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_random.test.js +3 -3
- package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
- package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
- package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
- package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
- package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
- package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
- package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
- package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
- package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
- package/out-tsc/test/temba-canvas-menu.test.js +122 -0
- package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
- package/out-tsc/test/temba-floating-tab.test.js +91 -0
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
- package/out-tsc/test/temba-floating-window.test.js +301 -0
- package/out-tsc/test/temba-floating-window.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +202 -2
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-flow-editor.test.js +7 -8
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
- package/out-tsc/test/temba-localization.test.js +471 -0
- package/out-tsc/test/temba-localization.test.js.map +1 -0
- package/out-tsc/test/temba-node-editor.test.js +3 -1
- package/out-tsc/test/temba-node-editor.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +265 -0
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +2 -1
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-sortable-list.test.js +51 -0
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/out-tsc/test/temba-utils-index.test.js +1 -27
- package/out-tsc/test/temba-utils-index.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +20 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +2 -1
- package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
- package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
- package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
- package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
- package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
- package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
- package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
- package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
- package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
- package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
- package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
- package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
- package/screenshots/truth/canvas-menu/open.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hidden.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/floating-window/chromeless.png +0 -0
- package/screenshots/truth/floating-window/custom-size.png +0 -0
- package/screenshots/truth/floating-window/default.png +0 -0
- package/screenshots/truth/floating-window/with-header.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
- package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/Icons.ts +4 -1
- package/src/display/FloatingTab.ts +174 -0
- package/src/display/ProgressBar.ts +22 -2
- package/src/events.ts +2 -8
- package/src/flow/CanvasMenu.ts +217 -0
- package/src/flow/CanvasNode.ts +596 -40
- package/src/flow/Editor.ts +1721 -45
- package/src/flow/NodeEditor.ts +621 -144
- package/src/flow/NodeTypeSelector.ts +636 -0
- package/src/flow/StickyNote.ts +12 -3
- package/src/flow/actions/add_contact_groups.ts +5 -4
- package/src/flow/actions/add_contact_urn.ts +78 -4
- package/src/flow/actions/add_input_labels.ts +5 -4
- package/src/flow/actions/play_audio.ts +3 -2
- package/src/flow/actions/remove_contact_groups.ts +16 -6
- package/src/flow/actions/request_optin.ts +3 -2
- package/src/flow/actions/say_msg.ts +3 -2
- package/src/flow/actions/send_broadcast.ts +86 -23
- package/src/flow/actions/send_email.ts +12 -6
- package/src/flow/actions/send_msg.ts +155 -34
- package/src/flow/actions/set_contact_channel.ts +6 -11
- package/src/flow/actions/set_contact_field.ts +21 -25
- package/src/flow/actions/set_contact_language.ts +11 -4
- package/src/flow/actions/set_contact_name.ts +4 -15
- package/src/flow/actions/set_contact_status.ts +4 -3
- package/src/flow/actions/set_run_result.ts +5 -4
- package/src/flow/actions/start_session.ts +210 -6
- package/src/flow/config.ts +11 -23
- package/src/flow/currencies.ts +51 -0
- package/src/flow/nodes/shared-rules.ts +301 -0
- package/src/flow/nodes/shared.ts +87 -0
- package/src/flow/nodes/split_by_airtime.ts +255 -5
- package/src/flow/nodes/split_by_contact_field.ts +195 -3
- package/src/flow/nodes/split_by_expression.ts +104 -2
- package/src/flow/nodes/split_by_groups.ts +26 -11
- package/src/flow/nodes/split_by_intent.ts +8 -0
- package/src/flow/nodes/split_by_llm.ts +22 -4
- package/src/flow/nodes/split_by_llm_categorize.ts +22 -5
- package/src/flow/nodes/split_by_random.ts +16 -6
- package/src/flow/nodes/split_by_resthook.ts +140 -0
- package/src/flow/nodes/split_by_run_result.ts +259 -3
- package/src/flow/nodes/split_by_scheme.ts +202 -2
- package/src/flow/nodes/split_by_subflow.ts +17 -5
- package/src/flow/nodes/split_by_ticket.ts +15 -4
- package/src/flow/nodes/split_by_webhook.ts +17 -6
- package/src/flow/nodes/wait_for_digits.ts +3 -2
- package/src/flow/nodes/wait_for_menu.ts +3 -2
- package/src/flow/nodes/wait_for_response.ts +59 -680
- package/src/flow/types.ts +156 -23
- package/src/flow/utils.ts +108 -14
- package/src/form/FieldRenderer.ts +2 -4
- package/src/interfaces.ts +3 -0
- package/src/layout/FloatingWindow.ts +386 -0
- package/src/list/SortableList.ts +109 -34
- package/src/live/ContactChat.ts +7 -25
- package/src/locales/es.ts +18 -13
- package/src/locales/fr.ts +18 -13
- package/src/locales/locale-codes.ts +11 -2
- package/src/locales/pt.ts +18 -13
- package/src/store/AppState.ts +173 -0
- package/src/store/flow-definition.d.ts +2 -5
- package/src/utils.ts +332 -12
- package/static/api/channels.json +46 -0
- package/static/api/llms.json +18 -0
- package/static/api/resthooks.json +31 -0
- package/static/svg/index.svg +1 -1
- package/static/svg/work/traced/lightning-02.svg +1 -0
- package/static/svg/work/used/lightning-02.svg +3 -0
- package/temba-modules.ts +8 -0
- package/test/ActionHelper.ts +3 -3
- package/test/NodeHelper.ts +6 -3
- package/test/actions/add_contact_urn.test.ts +287 -0
- package/test/actions/send_broadcast.test.ts +190 -0
- package/test/actions/send_email.test.ts +17 -23
- package/test/actions/send_msg.test.ts +39 -15
- package/test/actions/start_session.test.ts +151 -0
- package/test/nodes/split_by_airtime.test.ts +673 -0
- package/test/nodes/split_by_contact_field.test.ts +451 -0
- package/test/nodes/split_by_expression.test.ts +751 -0
- package/test/nodes/split_by_random.test.ts +3 -3
- package/test/nodes/split_by_resthook.test.ts +398 -0
- package/test/nodes/split_by_run_result.test.ts +1109 -0
- package/test/nodes/split_by_scheme.test.ts +486 -0
- package/test/nodes/split_by_subflow.test.ts +381 -0
- package/test/nodes/wait_for_digits.test.ts +2 -2
- package/test/nodes/wait_for_response.test.ts +2 -1
- package/test/temba-action-drag-between-nodes.test.ts +301 -0
- package/test/temba-canvas-menu.test.ts +156 -0
- package/test/temba-floating-tab.test.ts +110 -0
- package/test/temba-floating-window.test.ts +477 -0
- package/test/temba-flow-editor-node.test.ts +246 -2
- package/test/temba-flow-editor.test.ts +7 -8
- package/test/temba-localization.test.ts +611 -0
- package/test/temba-node-editor.test.ts +3 -1
- package/test/temba-node-type-selector.test.ts +355 -0
- package/test/temba-omnibox.test.ts +2 -1
- package/test/temba-sortable-list.test.ts +69 -0
- package/test/temba-utils-index.test.ts +0 -35
- package/test/utils.test.ts +22 -0
- package/test-assets/contacts/history.json +14 -21
- package/test-assets/select/llms.json +2 -2
- package/web-dev-server.config.mjs +49 -1
- package/web-test-runner.config.mjs +0 -1
- package/out-tsc/src/flow/actions/call_classifier.js +0 -11
- package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
- package/out-tsc/src/flow/actions/call_resthook.js +0 -11
- package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
- package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
- package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
- package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
- package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
- package/src/flow/actions/call_classifier.ts +0 -12
- package/src/flow/actions/call_resthook.ts +0 -12
- package/src/flow/actions/split_by_expression_example.ts +0 -88
- package/src/flow/actions/transfer_airtime.ts +0 -12
- package/src/flow/nodes/wait_for_audio.ts +0 -7
- package/src/flow/nodes/wait_for_image.ts +0 -7
- package/src/flow/nodes/wait_for_location.ts +0 -7
- package/src/flow/nodes/wait_for_video.ts +0 -7
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { expect, assert } from '@open-wc/testing';
|
|
2
|
+
import { NodeTypeSelector } from '../src/flow/NodeTypeSelector';
|
|
3
|
+
import { assertScreenshot, getClip, getComponent } from './utils.test';
|
|
4
|
+
|
|
5
|
+
describe('temba-node-type-selector', () => {
|
|
6
|
+
const createSelector = async () => {
|
|
7
|
+
const component = (await getComponent(
|
|
8
|
+
'temba-node-type-selector',
|
|
9
|
+
{},
|
|
10
|
+
'',
|
|
11
|
+
700,
|
|
12
|
+
600
|
|
13
|
+
)) as NodeTypeSelector;
|
|
14
|
+
await component.updateComplete;
|
|
15
|
+
return component;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
it('can be created', async () => {
|
|
19
|
+
const selector = await createSelector();
|
|
20
|
+
assert.instanceOf(selector, NodeTypeSelector);
|
|
21
|
+
expect(selector.open).to.be.false;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('is not visible when closed', async () => {
|
|
25
|
+
const selector = await createSelector();
|
|
26
|
+
expect(selector.open).to.be.false;
|
|
27
|
+
|
|
28
|
+
// component should not be in DOM when closed
|
|
29
|
+
expect(selector.hasAttribute('open')).to.be.false;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('shows dialog when opened in action mode', async () => {
|
|
33
|
+
const selector = await createSelector();
|
|
34
|
+
|
|
35
|
+
selector.show('action', { x: 100, y: 100 });
|
|
36
|
+
await selector.updateComplete;
|
|
37
|
+
|
|
38
|
+
expect(selector.open).to.be.true;
|
|
39
|
+
expect(selector.mode).to.equal('action');
|
|
40
|
+
expect(selector.hasAttribute('open')).to.be.true;
|
|
41
|
+
|
|
42
|
+
const dialog = selector.shadowRoot?.querySelector('.dialog') as HTMLElement;
|
|
43
|
+
await assertScreenshot('node-type-selector/action-mode', getClip(dialog));
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('shows dialog when opened in split mode', async () => {
|
|
47
|
+
const selector = await createSelector();
|
|
48
|
+
|
|
49
|
+
selector.show('split', { x: 100, y: 100 });
|
|
50
|
+
await selector.updateComplete;
|
|
51
|
+
|
|
52
|
+
expect(selector.open).to.be.true;
|
|
53
|
+
expect(selector.mode).to.equal('split');
|
|
54
|
+
|
|
55
|
+
const dialog = selector.shadowRoot?.querySelector('.dialog') as HTMLElement;
|
|
56
|
+
await assertScreenshot('node-type-selector/split-mode', getClip(dialog));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('displays action types in action mode', async () => {
|
|
60
|
+
const selector = await createSelector();
|
|
61
|
+
selector.show('action', { x: 100, y: 100 });
|
|
62
|
+
await selector.updateComplete;
|
|
63
|
+
|
|
64
|
+
const title = selector.shadowRoot?.querySelector('.header h2');
|
|
65
|
+
expect(title?.textContent).to.equal('Select an Action');
|
|
66
|
+
|
|
67
|
+
// verify we have node items
|
|
68
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item');
|
|
69
|
+
expect(nodeItems?.length).to.be.greaterThan(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('displays split types in split mode', async () => {
|
|
73
|
+
const selector = await createSelector();
|
|
74
|
+
selector.show('split', { x: 100, y: 100 });
|
|
75
|
+
await selector.updateComplete;
|
|
76
|
+
|
|
77
|
+
const title = selector.shadowRoot?.querySelector('.header h2');
|
|
78
|
+
expect(title?.textContent).to.equal('Select a Split');
|
|
79
|
+
|
|
80
|
+
// verify we have node items
|
|
81
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item');
|
|
82
|
+
expect(nodeItems?.length).to.be.greaterThan(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('closes when close() is called', async () => {
|
|
86
|
+
const selector = await createSelector();
|
|
87
|
+
selector.show('action', { x: 100, y: 100 });
|
|
88
|
+
await selector.updateComplete;
|
|
89
|
+
|
|
90
|
+
expect(selector.open).to.be.true;
|
|
91
|
+
|
|
92
|
+
selector.close();
|
|
93
|
+
await selector.updateComplete;
|
|
94
|
+
|
|
95
|
+
expect(selector.open).to.be.false;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('closes when overlay is clicked', async () => {
|
|
99
|
+
const selector = await createSelector();
|
|
100
|
+
selector.show('action', { x: 100, y: 100 });
|
|
101
|
+
await selector.updateComplete;
|
|
102
|
+
|
|
103
|
+
const overlay = selector.shadowRoot?.querySelector(
|
|
104
|
+
'.overlay'
|
|
105
|
+
) as HTMLElement;
|
|
106
|
+
overlay.click();
|
|
107
|
+
await selector.updateComplete;
|
|
108
|
+
|
|
109
|
+
expect(selector.open).to.be.false;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('closes when cancel button is clicked', async () => {
|
|
113
|
+
const selector = await createSelector();
|
|
114
|
+
selector.show('action', { x: 100, y: 100 });
|
|
115
|
+
await selector.updateComplete;
|
|
116
|
+
|
|
117
|
+
const cancelButton = selector.shadowRoot?.querySelector(
|
|
118
|
+
'temba-button'
|
|
119
|
+
) as HTMLElement;
|
|
120
|
+
cancelButton.click();
|
|
121
|
+
await selector.updateComplete;
|
|
122
|
+
|
|
123
|
+
expect(selector.open).to.be.false;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('fires selection event when node type is clicked', async () => {
|
|
127
|
+
const selector = await createSelector();
|
|
128
|
+
selector.show('action', { x: 100, y: 100 });
|
|
129
|
+
await selector.updateComplete;
|
|
130
|
+
|
|
131
|
+
let selectionFired = false;
|
|
132
|
+
let selectionDetail = null;
|
|
133
|
+
|
|
134
|
+
selector.addEventListener('temba-selection', (event: any) => {
|
|
135
|
+
selectionFired = true;
|
|
136
|
+
selectionDetail = event.detail;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// click on first node item
|
|
140
|
+
const firstNodeItem = selector.shadowRoot?.querySelector(
|
|
141
|
+
'.node-item'
|
|
142
|
+
) as HTMLElement;
|
|
143
|
+
firstNodeItem.click();
|
|
144
|
+
await selector.updateComplete;
|
|
145
|
+
|
|
146
|
+
expect(selectionFired).to.be.true;
|
|
147
|
+
expect(selectionDetail).to.have.property('nodeType');
|
|
148
|
+
expect(selectionDetail).to.have.property('position');
|
|
149
|
+
expect(selectionDetail.position).to.deep.equal({ x: 100, y: 100 });
|
|
150
|
+
expect(selector.open).to.be.false;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('filters actions by flow type - voice flow should show voice-only actions', async () => {
|
|
154
|
+
const selector = await createSelector();
|
|
155
|
+
selector.flowType = 'voice';
|
|
156
|
+
await selector.updateComplete;
|
|
157
|
+
selector.show('action', { x: 100, y: 100 });
|
|
158
|
+
await selector.updateComplete;
|
|
159
|
+
|
|
160
|
+
// get all node item titles
|
|
161
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
162
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
163
|
+
item.textContent?.trim()
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// voice flow should have Say Message and Play Audio
|
|
167
|
+
expect(titles).to.include('Say Message');
|
|
168
|
+
expect(titles).to.include('Play Audio');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('filters actions by flow type - message flow should not show voice-only actions', async () => {
|
|
172
|
+
const selector = await createSelector();
|
|
173
|
+
selector.flowType = 'message';
|
|
174
|
+
await selector.updateComplete;
|
|
175
|
+
selector.show('action', { x: 100, y: 100 });
|
|
176
|
+
await selector.updateComplete;
|
|
177
|
+
|
|
178
|
+
// get all node item titles
|
|
179
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
180
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
181
|
+
item.textContent?.trim()
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// message flow should not have Say Message or Play Audio
|
|
185
|
+
expect(titles).to.not.include('Say Message');
|
|
186
|
+
expect(titles).to.not.include('Play Audio');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('filters splits by flow type - message flow should show wait for response', async () => {
|
|
190
|
+
const selector = await createSelector();
|
|
191
|
+
selector.flowType = 'message';
|
|
192
|
+
await selector.updateComplete;
|
|
193
|
+
selector.show('split', { x: 100, y: 100 });
|
|
194
|
+
await selector.updateComplete;
|
|
195
|
+
|
|
196
|
+
// get all node item titles
|
|
197
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
198
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
199
|
+
item.textContent?.trim()
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// message flow should have Wait for Response
|
|
203
|
+
expect(titles).to.include('Wait for Response');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('filters splits by flow type - voice flow should not show wait for response', async () => {
|
|
207
|
+
const selector = await createSelector();
|
|
208
|
+
selector.flowType = 'voice';
|
|
209
|
+
await selector.updateComplete;
|
|
210
|
+
selector.show('split', { x: 100, y: 100 });
|
|
211
|
+
await selector.updateComplete;
|
|
212
|
+
|
|
213
|
+
// get all node item titles
|
|
214
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
215
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
216
|
+
item.textContent?.trim()
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// voice flow should not have Wait for Response
|
|
220
|
+
expect(titles).to.not.include('Wait for Response');
|
|
221
|
+
|
|
222
|
+
// but should have Wait for Digits and Wait for Menu Selection
|
|
223
|
+
expect(titles).to.include('Wait for Digits');
|
|
224
|
+
expect(titles).to.include('Wait for Menu Selection');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('filters by features - AI feature enables AI splits', async () => {
|
|
228
|
+
const selector = await createSelector();
|
|
229
|
+
selector.flowType = 'message';
|
|
230
|
+
selector.features = ['ai'];
|
|
231
|
+
await selector.updateComplete;
|
|
232
|
+
selector.show('split', { x: 100, y: 100 });
|
|
233
|
+
await selector.updateComplete;
|
|
234
|
+
|
|
235
|
+
// get all node item titles
|
|
236
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
237
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
238
|
+
item.textContent?.trim()
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// with ai feature, should have Split by AI
|
|
242
|
+
expect(titles).to.include('Split by AI');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('filters by features - without AI feature, AI splits are hidden', async () => {
|
|
246
|
+
const selector = await createSelector();
|
|
247
|
+
selector.flowType = 'message';
|
|
248
|
+
selector.features = [];
|
|
249
|
+
await selector.updateComplete;
|
|
250
|
+
selector.show('split', { x: 100, y: 100 });
|
|
251
|
+
await selector.updateComplete;
|
|
252
|
+
|
|
253
|
+
// get all node item titles
|
|
254
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
255
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
256
|
+
item.textContent?.trim()
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// without ai feature, should not have Split by AI
|
|
260
|
+
expect(titles).to.not.include('Split by AI');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('filters by features - airtime feature enables airtime actions', async () => {
|
|
264
|
+
const selector = await createSelector();
|
|
265
|
+
selector.flowType = 'message';
|
|
266
|
+
selector.features = ['airtime'];
|
|
267
|
+
await selector.updateComplete;
|
|
268
|
+
selector.show('action', { x: 100, y: 100 });
|
|
269
|
+
await selector.updateComplete;
|
|
270
|
+
|
|
271
|
+
// get all node item titles
|
|
272
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
273
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
274
|
+
item.textContent?.trim()
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// with airtime feature, should have Send Airtime
|
|
278
|
+
expect(titles).to.include('Send Airtime');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('filters by features - without airtime feature, airtime actions are hidden', async () => {
|
|
282
|
+
const selector = await createSelector();
|
|
283
|
+
selector.flowType = 'message';
|
|
284
|
+
selector.features = [];
|
|
285
|
+
await selector.updateComplete;
|
|
286
|
+
selector.show('action', { x: 100, y: 100 });
|
|
287
|
+
await selector.updateComplete;
|
|
288
|
+
|
|
289
|
+
// get all node item titles
|
|
290
|
+
const nodeItems = selector.shadowRoot?.querySelectorAll('.node-item-title');
|
|
291
|
+
const titles = Array.from(nodeItems || []).map((item) =>
|
|
292
|
+
item.textContent?.trim()
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// without airtime feature, should not have Send Airtime
|
|
296
|
+
expect(titles).to.not.include('Send Airtime');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('hides actions/nodes with empty flowTypes array from selector', async () => {
|
|
300
|
+
const selector = await createSelector();
|
|
301
|
+
|
|
302
|
+
// test that isConfigAvailable returns false for empty flowTypes array
|
|
303
|
+
const configWithEmptyFlowTypes = {
|
|
304
|
+
name: 'Test Action',
|
|
305
|
+
type: 'test_action',
|
|
306
|
+
flowTypes: [] // empty array should hide from all flow types
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
selector.flowType = 'message';
|
|
310
|
+
await selector.updateComplete;
|
|
311
|
+
|
|
312
|
+
// call private method via any to test behavior
|
|
313
|
+
const isAvailable = (selector as any).isConfigAvailable(
|
|
314
|
+
configWithEmptyFlowTypes
|
|
315
|
+
);
|
|
316
|
+
expect(isAvailable).to.be.false;
|
|
317
|
+
|
|
318
|
+
// test with different flow types - should still be false
|
|
319
|
+
selector.flowType = 'voice';
|
|
320
|
+
await selector.updateComplete;
|
|
321
|
+
|
|
322
|
+
const isAvailableVoice = (selector as any).isConfigAvailable(
|
|
323
|
+
configWithEmptyFlowTypes
|
|
324
|
+
);
|
|
325
|
+
expect(isAvailableVoice).to.be.false;
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('shows actions/nodes with undefined flowTypes for all flow types', async () => {
|
|
329
|
+
const selector = await createSelector();
|
|
330
|
+
|
|
331
|
+
// test that isConfigAvailable returns true for undefined flowTypes
|
|
332
|
+
const configWithUndefinedFlowTypes = {
|
|
333
|
+
name: 'Test Action',
|
|
334
|
+
type: 'test_action'
|
|
335
|
+
// flowTypes is undefined - should be available for all
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
selector.flowType = 'message';
|
|
339
|
+
await selector.updateComplete;
|
|
340
|
+
|
|
341
|
+
const isAvailable = (selector as any).isConfigAvailable(
|
|
342
|
+
configWithUndefinedFlowTypes
|
|
343
|
+
);
|
|
344
|
+
expect(isAvailable).to.be.true;
|
|
345
|
+
|
|
346
|
+
// test with different flow types - should still be true
|
|
347
|
+
selector.flowType = 'voice';
|
|
348
|
+
await selector.updateComplete;
|
|
349
|
+
|
|
350
|
+
const isAvailableVoice = (selector as any).isConfigAvailable(
|
|
351
|
+
configWithUndefinedFlowTypes
|
|
352
|
+
);
|
|
353
|
+
expect(isAvailableVoice).to.be.true;
|
|
354
|
+
});
|
|
355
|
+
});
|
|
@@ -49,7 +49,8 @@ describe('temba-omnibox', () => {
|
|
|
49
49
|
assert.instanceOf(omnibox, Omnibox);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
// TODO: make this pass reliably on CI
|
|
53
|
+
xit('fires change events on selection', async () => {
|
|
53
54
|
const omnibox: Omnibox = await createOmnibox(clock, {
|
|
54
55
|
endpoint: '/test-assets/select/omnibox.json'
|
|
55
56
|
});
|
|
@@ -148,4 +148,73 @@ describe('temba-sortable-list', () => {
|
|
|
148
148
|
const changeEvent = await updated;
|
|
149
149
|
expect(changeEvent.type).to.equal('change');
|
|
150
150
|
});
|
|
151
|
+
|
|
152
|
+
it('detects external drag when dragging outside container', async () => {
|
|
153
|
+
const list: SortableList = await createSorter(BORING_LIST);
|
|
154
|
+
list.externalDrag = true;
|
|
155
|
+
await list.updateComplete;
|
|
156
|
+
|
|
157
|
+
const bounds = list.getBoundingClientRect();
|
|
158
|
+
|
|
159
|
+
// track external drag events
|
|
160
|
+
let externalDragFired = false;
|
|
161
|
+
let internalDragFired = false;
|
|
162
|
+
|
|
163
|
+
list.addEventListener(CustomEventType.DragExternal, () => {
|
|
164
|
+
externalDragFired = true;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
list.addEventListener(CustomEventType.DragInternal, () => {
|
|
168
|
+
internalDragFired = true;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// start dragging an item
|
|
172
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
173
|
+
await mouseDown();
|
|
174
|
+
|
|
175
|
+
// drag outside the container (far to the right)
|
|
176
|
+
await moveMouse(bounds.right + 100, bounds.top + 10);
|
|
177
|
+
clock.runAll();
|
|
178
|
+
|
|
179
|
+
// should have fired external drag event
|
|
180
|
+
expect(externalDragFired).to.be.true;
|
|
181
|
+
|
|
182
|
+
// drag back inside
|
|
183
|
+
externalDragFired = false; // reset
|
|
184
|
+
await moveMouse(bounds.left + 20, bounds.top + 10);
|
|
185
|
+
clock.runAll();
|
|
186
|
+
|
|
187
|
+
// should have fired internal drag event
|
|
188
|
+
expect(internalDragFired).to.be.true;
|
|
189
|
+
|
|
190
|
+
// clean up
|
|
191
|
+
await mouseUp();
|
|
192
|
+
clock.runAll();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('fires DragStop with isExternal=true when dropped outside container', async () => {
|
|
196
|
+
const list: SortableList = await createSorter(BORING_LIST);
|
|
197
|
+
list.externalDrag = true;
|
|
198
|
+
await list.updateComplete;
|
|
199
|
+
|
|
200
|
+
const bounds = list.getBoundingClientRect();
|
|
201
|
+
|
|
202
|
+
// start dragging an item
|
|
203
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
204
|
+
await mouseDown();
|
|
205
|
+
|
|
206
|
+
// drag outside the container
|
|
207
|
+
await moveMouse(bounds.right + 100, bounds.top + 10);
|
|
208
|
+
clock.runAll();
|
|
209
|
+
|
|
210
|
+
// listen for drag stop event
|
|
211
|
+
const dragStop = oneEvent(list, CustomEventType.DragStop, false);
|
|
212
|
+
|
|
213
|
+
// drop outside
|
|
214
|
+
await mouseUp();
|
|
215
|
+
clock.runAll();
|
|
216
|
+
|
|
217
|
+
const dragStopEvent = await dragStop;
|
|
218
|
+
expect(dragStopEvent.detail.isExternal).to.be.true;
|
|
219
|
+
});
|
|
151
220
|
});
|
|
@@ -35,7 +35,6 @@ import {
|
|
|
35
35
|
stubbable,
|
|
36
36
|
renderAvatar,
|
|
37
37
|
fillTemplate,
|
|
38
|
-
spreadAttributes,
|
|
39
38
|
getUrl,
|
|
40
39
|
postUrl,
|
|
41
40
|
postJSON,
|
|
@@ -1098,40 +1097,6 @@ describe('utils/index', () => {
|
|
|
1098
1097
|
});
|
|
1099
1098
|
});
|
|
1100
1099
|
|
|
1101
|
-
describe('spreadAttributes', () => {
|
|
1102
|
-
it('spreads regular attributes', () => {
|
|
1103
|
-
const attrs = { id: 'test', class: 'example' };
|
|
1104
|
-
const result = spreadAttributes(attrs);
|
|
1105
|
-
|
|
1106
|
-
expect(Array.isArray(result)).to.be.true;
|
|
1107
|
-
expect(result.length).to.equal(2);
|
|
1108
|
-
});
|
|
1109
|
-
|
|
1110
|
-
it('handles event attributes with @', () => {
|
|
1111
|
-
const attrs = { '@click': 'handleClick' };
|
|
1112
|
-
const result = spreadAttributes(attrs);
|
|
1113
|
-
|
|
1114
|
-
expect(Array.isArray(result)).to.be.true;
|
|
1115
|
-
expect(result.length).to.equal(1);
|
|
1116
|
-
});
|
|
1117
|
-
|
|
1118
|
-
it('handles property attributes with .', () => {
|
|
1119
|
-
const attrs = { '.value': 'test' };
|
|
1120
|
-
const result = spreadAttributes(attrs);
|
|
1121
|
-
|
|
1122
|
-
expect(Array.isArray(result)).to.be.true;
|
|
1123
|
-
expect(result.length).to.equal(1);
|
|
1124
|
-
});
|
|
1125
|
-
|
|
1126
|
-
it('handles mixed attribute types', () => {
|
|
1127
|
-
const attrs = { id: 'test', '@click': 'handler', '.prop': 'value' };
|
|
1128
|
-
const result = spreadAttributes(attrs);
|
|
1129
|
-
|
|
1130
|
-
expect(Array.isArray(result)).to.be.true;
|
|
1131
|
-
expect(result.length).to.equal(3);
|
|
1132
|
-
});
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
1100
|
describe('renderAvatar', () => {
|
|
1136
1101
|
it('renders avatar with name only', () => {
|
|
1137
1102
|
const result = renderAvatar({ name: 'John Doe' });
|
package/test/utils.test.ts
CHANGED
|
@@ -8,6 +8,26 @@ interface Clip {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
import { expect, fixture, html, assert } from '@open-wc/testing';
|
|
11
|
+
|
|
12
|
+
// disable transitions for all tests to prevent flaky screenshot tests
|
|
13
|
+
const style = document.createElement('style');
|
|
14
|
+
style.textContent = `
|
|
15
|
+
* {
|
|
16
|
+
--transition-duration: 0ms !important;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
document.head.appendChild(style);
|
|
20
|
+
|
|
21
|
+
// prevent resize event listeners from being added during tests
|
|
22
|
+
// this prevents flaky positioning in components that adjust on resize
|
|
23
|
+
const originalAddEventListener = window.addEventListener;
|
|
24
|
+
window.addEventListener = function (type, listener, options) {
|
|
25
|
+
if (type === 'resize') {
|
|
26
|
+
// skip adding resize listeners during tests
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
return originalAddEventListener.call(this, type, listener, options);
|
|
30
|
+
} as typeof window.addEventListener;
|
|
11
31
|
import MouseHelper from './MouseHelper';
|
|
12
32
|
import { Store } from '../src/store/Store';
|
|
13
33
|
import { stub } from 'sinon';
|
|
@@ -330,6 +350,7 @@ export const clickOption = async (
|
|
|
330
350
|
);
|
|
331
351
|
if (!existingOption) {
|
|
332
352
|
try {
|
|
353
|
+
// Increased wait time to handle slower CI environments
|
|
333
354
|
await waitForCondition(
|
|
334
355
|
() => {
|
|
335
356
|
const option = options.shadowRoot?.querySelector(
|
|
@@ -376,6 +397,7 @@ export const openSelect = async (clock: any, select: Select<SelectOption>) => {
|
|
|
376
397
|
if (hasEndpoint) {
|
|
377
398
|
try {
|
|
378
399
|
// Wait for options to be properly rendered and visible (but only for endpoint selects)
|
|
400
|
+
// Increased max attempts to handle slower CI environments
|
|
379
401
|
await waitForCondition(
|
|
380
402
|
() => {
|
|
381
403
|
const options = select.shadowRoot.querySelector(
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
"recent_only": false,
|
|
4
4
|
"next_before": 1617135091567814,
|
|
5
5
|
"next_after": 1609359091567814,
|
|
6
|
-
"start_date": "2019-09-22",
|
|
7
6
|
"events": [
|
|
8
7
|
{
|
|
9
8
|
"uuid": "01997d74-bf67-749a-8440-688a41c3b275",
|
|
@@ -55,10 +54,8 @@
|
|
|
55
54
|
"name": "SMS Channel"
|
|
56
55
|
}
|
|
57
56
|
},
|
|
58
|
-
"status": "
|
|
59
|
-
"
|
|
60
|
-
"failed_reason_display": "No suitable channel found",
|
|
61
|
-
"logs_url": null
|
|
57
|
+
"_status": {"created_on": "2025-09-24T20:40:28.239437+00:00", "status": "failed", "reason": "error_limit"},
|
|
58
|
+
"_logs_url": null
|
|
62
59
|
},
|
|
63
60
|
{
|
|
64
61
|
"uuid": "01997d74-bf67-7305-b5f7-017c6eb04cf7",
|
|
@@ -92,8 +89,8 @@
|
|
|
92
89
|
"name": "SMS Channel"
|
|
93
90
|
}
|
|
94
91
|
},
|
|
95
|
-
"status": "
|
|
96
|
-
"
|
|
92
|
+
"_status": {"created_on": "2025-09-23T20:40:28.239434+00:00", "status": "wired"},
|
|
93
|
+
"_logs_url": "/channels/channellog/read/1478/"
|
|
97
94
|
},
|
|
98
95
|
{
|
|
99
96
|
"uuid": "01997d74-bf67-74a1-97db-faaf45bbff53",
|
|
@@ -107,8 +104,7 @@
|
|
|
107
104
|
"name": "SMS Channel"
|
|
108
105
|
}
|
|
109
106
|
},
|
|
110
|
-
"
|
|
111
|
-
"logs_url": "/channels/channellog/read/1477/"
|
|
107
|
+
"_logs_url": "/channels/channellog/read/1477/"
|
|
112
108
|
},
|
|
113
109
|
{
|
|
114
110
|
"uuid": "01997d74-bf67-70c2-bfae-14c661955736",
|
|
@@ -122,8 +118,8 @@
|
|
|
122
118
|
"name": "SMS Channel"
|
|
123
119
|
}
|
|
124
120
|
},
|
|
125
|
-
"status": "
|
|
126
|
-
"
|
|
121
|
+
"_status": {"created_on": "2025-09-23T20:40:28.239434+00:00", "status": "wired"},
|
|
122
|
+
"_logs_url": "/channels/channellog/read/1476/"
|
|
127
123
|
},
|
|
128
124
|
{
|
|
129
125
|
"uuid": "01997d74-bf67-7199-8e8a-200e41a90d71",
|
|
@@ -137,8 +133,7 @@
|
|
|
137
133
|
"name": "SMS Channel"
|
|
138
134
|
}
|
|
139
135
|
},
|
|
140
|
-
"
|
|
141
|
-
"logs_url": "/channels/channellog/read/1475/"
|
|
136
|
+
"_logs_url": "/channels/channellog/read/1475/"
|
|
142
137
|
},
|
|
143
138
|
{
|
|
144
139
|
"uuid": "01997d74-bf67-7768-9b3e-1a2dd9b90aa4",
|
|
@@ -152,8 +147,8 @@
|
|
|
152
147
|
"name": "SMS Channel"
|
|
153
148
|
}
|
|
154
149
|
},
|
|
155
|
-
"status": "
|
|
156
|
-
"
|
|
150
|
+
"_status": {"created_on": "2025-09-23T20:40:28.239434+00:00", "status": "wired"},
|
|
151
|
+
"_logs_url": "/channels/channellog/read/1474/"
|
|
157
152
|
},
|
|
158
153
|
{
|
|
159
154
|
"uuid": "01997d74-bf67-74c6-86d6-a9398ee1f5f4",
|
|
@@ -167,8 +162,7 @@
|
|
|
167
162
|
"name": "SMS Channel"
|
|
168
163
|
}
|
|
169
164
|
},
|
|
170
|
-
"
|
|
171
|
-
"logs_url": "/channels/channellog/read/1473/"
|
|
165
|
+
"_logs_url": "/channels/channellog/read/1473/"
|
|
172
166
|
},
|
|
173
167
|
{
|
|
174
168
|
"uuid": "01997d74-bf67-75c9-bd59-36a465fc0c88",
|
|
@@ -204,8 +198,8 @@
|
|
|
204
198
|
"name": "SMS Channel"
|
|
205
199
|
}
|
|
206
200
|
},
|
|
207
|
-
"status": "
|
|
208
|
-
"
|
|
201
|
+
"_status": {"created_on": "2025-09-23T20:40:28.239434+00:00", "status": "wired"},
|
|
202
|
+
"_logs_url": "/channels/channellog/read/1472/"
|
|
209
203
|
},
|
|
210
204
|
{
|
|
211
205
|
"uuid": "01997d74-bf67-70a9-a0fc-6543b4d0b3d1",
|
|
@@ -234,8 +228,7 @@
|
|
|
234
228
|
"name": "SMS Channel"
|
|
235
229
|
}
|
|
236
230
|
},
|
|
237
|
-
"
|
|
238
|
-
"logs_url": "/channels/channellog/read/1471/"
|
|
231
|
+
"_logs_url": "/channels/channellog/read/1471/"
|
|
239
232
|
}
|
|
240
233
|
]
|
|
241
234
|
}
|