@nyaruka/temba-components 0.131.0 → 0.131.2
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 +67 -1
- package/demo/data/flows/food-order.json +2 -2
- package/demo/data/flows/sample-flow.json +74 -125
- package/dist/static/svg/index.svg +1 -1
- package/dist/temba-components.js +1156 -619
- 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/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 +327 -19
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +562 -66
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +240 -93
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +499 -0
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
- package/out-tsc/src/flow/actions/add_contact_groups.js +3 -3
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +62 -4
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +3 -3
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +2 -2
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -5
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +2 -2
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -2
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +76 -23
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +4 -5
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +9 -19
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +5 -9
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +19 -20
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -12
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -2
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +3 -3
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +180 -6
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -15
- 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 +17 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_airtime.js +205 -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 +147 -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 +68 -2
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +12 -9
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +7 -0
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
- package/out-tsc/src/flow/nodes/split_by_llm.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +2 -2
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +3 -3
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +108 -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 +206 -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 +153 -2
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +6 -4
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +32 -567
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +2 -2
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -1
- package/out-tsc/src/flow/types.js +71 -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/ContactSearch.js +1 -1
- package/out-tsc/src/form/ContactSearch.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/list/SortableList.js +98 -33
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/live/ContactChat.js +15 -18
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/store/AppState.js +53 -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 +4 -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-flow-editor-node.test.js +85 -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-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 +115 -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 +2 -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/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/events.ts +2 -6
- package/src/flow/CanvasMenu.ts +217 -0
- package/src/flow/CanvasNode.ts +408 -10
- package/src/flow/Editor.ts +683 -44
- package/src/flow/NodeEditor.ts +304 -125
- package/src/flow/NodeTypeSelector.ts +592 -0
- package/src/flow/actions/add_contact_groups.ts +4 -4
- package/src/flow/actions/add_contact_urn.ts +76 -4
- package/src/flow/actions/add_input_labels.ts +4 -4
- package/src/flow/actions/play_audio.ts +2 -2
- package/src/flow/actions/remove_contact_groups.ts +14 -6
- package/src/flow/actions/request_optin.ts +2 -2
- package/src/flow/actions/say_msg.ts +2 -2
- package/src/flow/actions/send_broadcast.ts +85 -23
- package/src/flow/actions/send_email.ts +10 -6
- package/src/flow/actions/send_msg.ts +22 -32
- package/src/flow/actions/set_contact_channel.ts +5 -11
- package/src/flow/actions/set_contact_field.ts +20 -25
- package/src/flow/actions/set_contact_language.ts +9 -4
- package/src/flow/actions/set_contact_name.ts +3 -15
- package/src/flow/actions/set_contact_status.ts +3 -3
- package/src/flow/actions/set_run_result.ts +4 -4
- package/src/flow/actions/start_session.ts +208 -6
- package/src/flow/config.ts +13 -15
- package/src/flow/currencies.ts +51 -0
- package/src/flow/nodes/shared-rules.ts +301 -0
- package/src/flow/nodes/shared.ts +18 -0
- package/src/flow/nodes/split_by_airtime.ts +238 -5
- package/src/flow/nodes/split_by_contact_field.ts +185 -3
- package/src/flow/nodes/split_by_expression.ts +94 -2
- package/src/flow/nodes/split_by_groups.ts +15 -10
- package/src/flow/nodes/split_by_intent.ts +7 -0
- package/src/flow/nodes/split_by_llm.ts +4 -3
- package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
- package/src/flow/nodes/split_by_random.ts +5 -5
- package/src/flow/nodes/split_by_resthook.ts +130 -0
- package/src/flow/nodes/split_by_run_result.ts +249 -3
- package/src/flow/nodes/split_by_scheme.ts +192 -2
- package/src/flow/nodes/split_by_subflow.ts +6 -4
- package/src/flow/nodes/split_by_ticket.ts +4 -3
- package/src/flow/nodes/split_by_webhook.ts +6 -5
- package/src/flow/nodes/wait_for_audio.ts +2 -2
- package/src/flow/nodes/wait_for_digits.ts +2 -2
- package/src/flow/nodes/wait_for_image.ts +2 -2
- package/src/flow/nodes/wait_for_location.ts +2 -2
- package/src/flow/nodes/wait_for_menu.ts +2 -2
- package/src/flow/nodes/wait_for_response.ts +48 -679
- package/src/flow/nodes/wait_for_video.ts +2 -2
- package/src/flow/types.ts +109 -23
- package/src/flow/utils.ts +108 -14
- package/src/form/ContactSearch.ts +1 -1
- package/src/form/FieldRenderer.ts +2 -4
- package/src/interfaces.ts +3 -0
- package/src/list/SortableList.ts +109 -34
- package/src/live/ContactChat.ts +15 -18
- package/src/store/AppState.ts +69 -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/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 +4 -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-flow-editor-node.test.ts +102 -2
- package/test/temba-flow-editor.test.ts +7 -8
- package/test/temba-node-editor.test.ts +3 -1
- package/test/temba-node-type-selector.test.ts +152 -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 +2 -0
- package/test-assets/contacts/history.json +14 -20
- package/web-dev-server.config.mjs +3 -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/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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { expect, fixture, html } from '@open-wc/testing';
|
|
2
|
+
import '../temba-modules';
|
|
3
|
+
describe('Drag actions between nodes', () => {
|
|
4
|
+
let node1;
|
|
5
|
+
let node1UI;
|
|
6
|
+
let node2;
|
|
7
|
+
let node2UI;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Create test nodes
|
|
10
|
+
node1 = {
|
|
11
|
+
uuid: 'node-1',
|
|
12
|
+
actions: [
|
|
13
|
+
{
|
|
14
|
+
type: 'send_msg',
|
|
15
|
+
uuid: 'action-1',
|
|
16
|
+
text: 'First message',
|
|
17
|
+
quick_replies: []
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'send_msg',
|
|
21
|
+
uuid: 'action-2',
|
|
22
|
+
text: 'Second message',
|
|
23
|
+
quick_replies: []
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }]
|
|
27
|
+
};
|
|
28
|
+
node1UI = {
|
|
29
|
+
position: { left: 100, top: 100 },
|
|
30
|
+
type: 'execute_actions',
|
|
31
|
+
config: {}
|
|
32
|
+
};
|
|
33
|
+
node2 = {
|
|
34
|
+
uuid: 'node-2',
|
|
35
|
+
actions: [
|
|
36
|
+
{
|
|
37
|
+
type: 'send_msg',
|
|
38
|
+
uuid: 'action-3',
|
|
39
|
+
text: 'Third message',
|
|
40
|
+
quick_replies: []
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
exits: [{ uuid: 'exit-2', destination_uuid: null }]
|
|
44
|
+
};
|
|
45
|
+
node2UI = {
|
|
46
|
+
position: { left: 400, top: 100 },
|
|
47
|
+
type: 'execute_actions',
|
|
48
|
+
config: {}
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
it('should render execute_actions node with sortable list', async () => {
|
|
52
|
+
const node1Element = await fixture(html `
|
|
53
|
+
<temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>
|
|
54
|
+
`);
|
|
55
|
+
await node1Element.updateComplete;
|
|
56
|
+
// Verify node renders with sortable list
|
|
57
|
+
const sortableList = node1Element.querySelector('temba-sortable-list');
|
|
58
|
+
expect(sortableList).to.exist;
|
|
59
|
+
// Verify actions are rendered
|
|
60
|
+
const actions = node1Element.querySelectorAll('.action.sortable');
|
|
61
|
+
expect(actions.length).to.equal(2);
|
|
62
|
+
});
|
|
63
|
+
it('should show placeholder in target node during drag', async () => {
|
|
64
|
+
const node2Element = await fixture(html `
|
|
65
|
+
<temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>
|
|
66
|
+
`);
|
|
67
|
+
await node2Element.updateComplete;
|
|
68
|
+
// Simulate action-drag-over event from Editor
|
|
69
|
+
const dragOverEvent = new CustomEvent('action-drag-over', {
|
|
70
|
+
detail: {
|
|
71
|
+
action: node1.actions[0],
|
|
72
|
+
sourceNodeUuid: 'node-1',
|
|
73
|
+
actionIndex: 0,
|
|
74
|
+
mouseY: 150
|
|
75
|
+
},
|
|
76
|
+
bubbles: false
|
|
77
|
+
});
|
|
78
|
+
node2Element.dispatchEvent(dragOverEvent);
|
|
79
|
+
await node2Element.updateComplete;
|
|
80
|
+
// Check that placeholder is rendered
|
|
81
|
+
const placeholder = node2Element.querySelector('.drop-placeholder');
|
|
82
|
+
expect(placeholder).to.exist;
|
|
83
|
+
});
|
|
84
|
+
it('should handle drag-over event and store external drag info', async () => {
|
|
85
|
+
const node2Element = await fixture(html `
|
|
86
|
+
<temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>
|
|
87
|
+
`);
|
|
88
|
+
await node2Element.updateComplete;
|
|
89
|
+
// Simulate drag over
|
|
90
|
+
const dragOverEvent = new CustomEvent('action-drag-over', {
|
|
91
|
+
detail: {
|
|
92
|
+
action: node1.actions[0],
|
|
93
|
+
sourceNodeUuid: 'node-1',
|
|
94
|
+
actionIndex: 0,
|
|
95
|
+
mouseY: 150
|
|
96
|
+
},
|
|
97
|
+
bubbles: false
|
|
98
|
+
});
|
|
99
|
+
node2Element.dispatchEvent(dragOverEvent);
|
|
100
|
+
await node2Element.updateComplete;
|
|
101
|
+
// Verify that external drag info is stored (check internal state)
|
|
102
|
+
const externalDragInfo = node2Element.externalDragInfo;
|
|
103
|
+
expect(externalDragInfo).to.exist;
|
|
104
|
+
expect(externalDragInfo.action.uuid).to.equal('action-1');
|
|
105
|
+
expect(externalDragInfo.sourceNodeUuid).to.equal('node-1');
|
|
106
|
+
});
|
|
107
|
+
it('should calculate correct drop index based on mouse position', async () => {
|
|
108
|
+
const node2Element = await fixture(html `
|
|
109
|
+
<temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>
|
|
110
|
+
`);
|
|
111
|
+
await node2Element.updateComplete;
|
|
112
|
+
// Get action element bounds to calculate positions
|
|
113
|
+
const actionElement = node2Element.querySelector('.action.sortable');
|
|
114
|
+
expect(actionElement).to.exist;
|
|
115
|
+
const rect = actionElement.getBoundingClientRect();
|
|
116
|
+
const topY = rect.top + 5; // Near top of first action
|
|
117
|
+
const bottomY = rect.bottom + 5; // Below first action
|
|
118
|
+
// Drag over at top
|
|
119
|
+
const dragOverEventTop = new CustomEvent('action-drag-over', {
|
|
120
|
+
detail: {
|
|
121
|
+
action: node1.actions[0],
|
|
122
|
+
sourceNodeUuid: 'node-1',
|
|
123
|
+
actionIndex: 0,
|
|
124
|
+
mouseY: topY
|
|
125
|
+
},
|
|
126
|
+
bubbles: false
|
|
127
|
+
});
|
|
128
|
+
node2Element.dispatchEvent(dragOverEventTop);
|
|
129
|
+
await node2Element.updateComplete;
|
|
130
|
+
// Check drop index is at beginning
|
|
131
|
+
let externalDragInfo = node2Element.externalDragInfo;
|
|
132
|
+
expect(externalDragInfo.dropIndex).to.equal(0);
|
|
133
|
+
// Drag over at bottom
|
|
134
|
+
const dragOverEventBottom = new CustomEvent('action-drag-over', {
|
|
135
|
+
detail: {
|
|
136
|
+
action: node1.actions[0],
|
|
137
|
+
sourceNodeUuid: 'node-1',
|
|
138
|
+
actionIndex: 0,
|
|
139
|
+
mouseY: bottomY
|
|
140
|
+
},
|
|
141
|
+
bubbles: false
|
|
142
|
+
});
|
|
143
|
+
node2Element.dispatchEvent(dragOverEventBottom);
|
|
144
|
+
await node2Element.updateComplete;
|
|
145
|
+
// Check drop index is at end
|
|
146
|
+
externalDragInfo = node2Element.externalDragInfo;
|
|
147
|
+
expect(externalDragInfo.dropIndex).to.equal(1);
|
|
148
|
+
});
|
|
149
|
+
it('should not accept drops from the same node', async () => {
|
|
150
|
+
const node1Element = await fixture(html `
|
|
151
|
+
<temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>
|
|
152
|
+
`);
|
|
153
|
+
await node1Element.updateComplete;
|
|
154
|
+
// Try to drop action from node-1 onto node-1
|
|
155
|
+
const dragOverEvent = new CustomEvent('action-drag-over', {
|
|
156
|
+
detail: {
|
|
157
|
+
action: node1.actions[0],
|
|
158
|
+
sourceNodeUuid: 'node-1', // Same as target
|
|
159
|
+
actionIndex: 0,
|
|
160
|
+
mouseY: 150
|
|
161
|
+
},
|
|
162
|
+
bubbles: false
|
|
163
|
+
});
|
|
164
|
+
node1Element.dispatchEvent(dragOverEvent);
|
|
165
|
+
await node1Element.updateComplete;
|
|
166
|
+
// Verify external drag info was NOT stored (rejected due to same source)
|
|
167
|
+
const externalDragInfo = node1Element.externalDragInfo;
|
|
168
|
+
expect(externalDragInfo).to.be.null;
|
|
169
|
+
});
|
|
170
|
+
it('should clear external drag state and hide placeholder', async () => {
|
|
171
|
+
const node2Element = await fixture(html `
|
|
172
|
+
<temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>
|
|
173
|
+
`);
|
|
174
|
+
await node2Element.updateComplete;
|
|
175
|
+
// Simulate drag over
|
|
176
|
+
const dragOverEvent = new CustomEvent('action-drag-over', {
|
|
177
|
+
detail: {
|
|
178
|
+
action: node1.actions[0],
|
|
179
|
+
sourceNodeUuid: 'node-1',
|
|
180
|
+
actionIndex: 0,
|
|
181
|
+
mouseY: 150
|
|
182
|
+
},
|
|
183
|
+
bubbles: false
|
|
184
|
+
});
|
|
185
|
+
node2Element.dispatchEvent(dragOverEvent);
|
|
186
|
+
await node2Element.updateComplete;
|
|
187
|
+
// Verify placeholder exists
|
|
188
|
+
let placeholder = node2Element.querySelector('.drop-placeholder');
|
|
189
|
+
expect(placeholder).to.exist;
|
|
190
|
+
// Clear the external drag state (simulating drag leaving the node)
|
|
191
|
+
node2Element.externalDragInfo = null;
|
|
192
|
+
// Trigger re-render
|
|
193
|
+
node2Element.requestUpdate();
|
|
194
|
+
await node2Element.updateComplete;
|
|
195
|
+
// Verify placeholder is gone
|
|
196
|
+
placeholder = node2Element.querySelector('.drop-placeholder');
|
|
197
|
+
expect(placeholder).to.not.exist;
|
|
198
|
+
});
|
|
199
|
+
it('should have sortable list for internal drag support', async () => {
|
|
200
|
+
const node1Element = await fixture(html `
|
|
201
|
+
<temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>
|
|
202
|
+
`);
|
|
203
|
+
await node1Element.updateComplete;
|
|
204
|
+
// Verify sortable list exists for internal drag (reordering within same node)
|
|
205
|
+
const sortableList = node1Element.querySelector('temba-sortable-list');
|
|
206
|
+
expect(sortableList).to.exist;
|
|
207
|
+
// Verify it has external drag enabled
|
|
208
|
+
expect(sortableList.hasAttribute('externaldrag')).to.be.true;
|
|
209
|
+
});
|
|
210
|
+
it('should only accept drops on execute_actions nodes', async () => {
|
|
211
|
+
const routerNodeUI = {
|
|
212
|
+
position: { left: 100, top: 100 },
|
|
213
|
+
type: 'split_by_expression', // Not execute_actions
|
|
214
|
+
config: {}
|
|
215
|
+
};
|
|
216
|
+
const routerNode = {
|
|
217
|
+
uuid: 'router-node',
|
|
218
|
+
actions: [],
|
|
219
|
+
exits: [{ uuid: 'exit-1', destination_uuid: null }],
|
|
220
|
+
router: {
|
|
221
|
+
type: 'switch',
|
|
222
|
+
operand: '@input',
|
|
223
|
+
cases: [],
|
|
224
|
+
categories: [],
|
|
225
|
+
default_category_uuid: 'cat-1'
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const routerElement = await fixture(html `
|
|
229
|
+
<temba-flow-node
|
|
230
|
+
.node=${routerNode}
|
|
231
|
+
.ui=${routerNodeUI}
|
|
232
|
+
></temba-flow-node>
|
|
233
|
+
`);
|
|
234
|
+
await routerElement.updateComplete;
|
|
235
|
+
// Try to drag over a non-execute_actions node
|
|
236
|
+
const dragOverEvent = new CustomEvent('action-drag-over', {
|
|
237
|
+
detail: {
|
|
238
|
+
action: node1.actions[0],
|
|
239
|
+
sourceNodeUuid: 'node-1',
|
|
240
|
+
actionIndex: 0,
|
|
241
|
+
mouseY: 150
|
|
242
|
+
},
|
|
243
|
+
bubbles: false
|
|
244
|
+
});
|
|
245
|
+
routerElement.dispatchEvent(dragOverEvent);
|
|
246
|
+
await routerElement.updateComplete;
|
|
247
|
+
// Verify external drag info was NOT stored (rejected due to wrong node type)
|
|
248
|
+
const externalDragInfo = routerElement.externalDragInfo;
|
|
249
|
+
expect(externalDragInfo).to.be.null;
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
//# sourceMappingURL=temba-action-drag-between-nodes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temba-action-drag-between-nodes.test.js","sourceRoot":"","sources":["../../test/temba-action-drag-between-nodes.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, html } from '@open-wc/testing';\nimport { CanvasNode } from '../src/flow/CanvasNode';\nimport { Node, NodeUI, SendMsg } from '../src/store/flow-definition';\nimport '../temba-modules';\n\ndescribe('Drag actions between nodes', () => {\n let node1: Node;\n let node1UI: NodeUI;\n let node2: Node;\n let node2UI: NodeUI;\n\n beforeEach(() => {\n // Create test nodes\n node1 = {\n uuid: 'node-1',\n actions: [\n {\n type: 'send_msg',\n uuid: 'action-1',\n text: 'First message',\n quick_replies: []\n } as SendMsg,\n {\n type: 'send_msg',\n uuid: 'action-2',\n text: 'Second message',\n quick_replies: []\n } as SendMsg\n ],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n\n node1UI = {\n position: { left: 100, top: 100 },\n type: 'execute_actions',\n config: {}\n };\n\n node2 = {\n uuid: 'node-2',\n actions: [\n {\n type: 'send_msg',\n uuid: 'action-3',\n text: 'Third message',\n quick_replies: []\n } as SendMsg\n ],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n\n node2UI = {\n position: { left: 400, top: 100 },\n type: 'execute_actions',\n config: {}\n };\n });\n\n it('should render execute_actions node with sortable list', async () => {\n const node1Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>\n `);\n\n await node1Element.updateComplete;\n\n // Verify node renders with sortable list\n const sortableList = node1Element.querySelector('temba-sortable-list');\n expect(sortableList).to.exist;\n\n // Verify actions are rendered\n const actions = node1Element.querySelectorAll('.action.sortable');\n expect(actions.length).to.equal(2);\n });\n\n it('should show placeholder in target node during drag', async () => {\n const node2Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>\n `);\n\n await node2Element.updateComplete;\n\n // Simulate action-drag-over event from Editor\n const dragOverEvent = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: 150\n },\n bubbles: false\n });\n\n node2Element.dispatchEvent(dragOverEvent);\n await node2Element.updateComplete;\n\n // Check that placeholder is rendered\n const placeholder = node2Element.querySelector('.drop-placeholder');\n expect(placeholder).to.exist;\n });\n\n it('should handle drag-over event and store external drag info', async () => {\n const node2Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>\n `);\n\n await node2Element.updateComplete;\n\n // Simulate drag over\n const dragOverEvent = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: 150\n },\n bubbles: false\n });\n node2Element.dispatchEvent(dragOverEvent);\n await node2Element.updateComplete;\n\n // Verify that external drag info is stored (check internal state)\n const externalDragInfo = (node2Element as any).externalDragInfo;\n expect(externalDragInfo).to.exist;\n expect(externalDragInfo.action.uuid).to.equal('action-1');\n expect(externalDragInfo.sourceNodeUuid).to.equal('node-1');\n });\n\n it('should calculate correct drop index based on mouse position', async () => {\n const node2Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>\n `);\n\n await node2Element.updateComplete;\n\n // Get action element bounds to calculate positions\n const actionElement = node2Element.querySelector(\n '.action.sortable'\n ) as HTMLElement;\n expect(actionElement).to.exist;\n\n const rect = actionElement.getBoundingClientRect();\n const topY = rect.top + 5; // Near top of first action\n const bottomY = rect.bottom + 5; // Below first action\n\n // Drag over at top\n const dragOverEventTop = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: topY\n },\n bubbles: false\n });\n node2Element.dispatchEvent(dragOverEventTop);\n await node2Element.updateComplete;\n\n // Check drop index is at beginning\n let externalDragInfo = (node2Element as any).externalDragInfo;\n expect(externalDragInfo.dropIndex).to.equal(0);\n\n // Drag over at bottom\n const dragOverEventBottom = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: bottomY\n },\n bubbles: false\n });\n node2Element.dispatchEvent(dragOverEventBottom);\n await node2Element.updateComplete;\n\n // Check drop index is at end\n externalDragInfo = (node2Element as any).externalDragInfo;\n expect(externalDragInfo.dropIndex).to.equal(1);\n });\n\n it('should not accept drops from the same node', async () => {\n const node1Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>\n `);\n\n await node1Element.updateComplete;\n\n // Try to drop action from node-1 onto node-1\n const dragOverEvent = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1', // Same as target\n actionIndex: 0,\n mouseY: 150\n },\n bubbles: false\n });\n node1Element.dispatchEvent(dragOverEvent);\n await node1Element.updateComplete;\n\n // Verify external drag info was NOT stored (rejected due to same source)\n const externalDragInfo = (node1Element as any).externalDragInfo;\n expect(externalDragInfo).to.be.null;\n });\n\n it('should clear external drag state and hide placeholder', async () => {\n const node2Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node2} .ui=${node2UI}></temba-flow-node>\n `);\n\n await node2Element.updateComplete;\n\n // Simulate drag over\n const dragOverEvent = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: 150\n },\n bubbles: false\n });\n node2Element.dispatchEvent(dragOverEvent);\n await node2Element.updateComplete;\n\n // Verify placeholder exists\n let placeholder = node2Element.querySelector('.drop-placeholder');\n expect(placeholder).to.exist;\n\n // Clear the external drag state (simulating drag leaving the node)\n (node2Element as any).externalDragInfo = null;\n // Trigger re-render\n node2Element.requestUpdate();\n await node2Element.updateComplete;\n\n // Verify placeholder is gone\n placeholder = node2Element.querySelector('.drop-placeholder');\n expect(placeholder).to.not.exist;\n });\n\n it('should have sortable list for internal drag support', async () => {\n const node1Element = await fixture<CanvasNode>(html`\n <temba-flow-node .node=${node1} .ui=${node1UI}></temba-flow-node>\n `);\n\n await node1Element.updateComplete;\n\n // Verify sortable list exists for internal drag (reordering within same node)\n const sortableList = node1Element.querySelector('temba-sortable-list');\n expect(sortableList).to.exist;\n\n // Verify it has external drag enabled\n expect(sortableList.hasAttribute('externaldrag')).to.be.true;\n });\n\n it('should only accept drops on execute_actions nodes', async () => {\n const routerNodeUI: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'split_by_expression', // Not execute_actions\n config: {}\n };\n\n const routerNode: Node = {\n uuid: 'router-node',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }],\n router: {\n type: 'switch',\n operand: '@input',\n cases: [],\n categories: [],\n default_category_uuid: 'cat-1'\n }\n };\n\n const routerElement = await fixture<CanvasNode>(html`\n <temba-flow-node\n .node=${routerNode}\n .ui=${routerNodeUI}\n ></temba-flow-node>\n `);\n\n await routerElement.updateComplete;\n\n // Try to drag over a non-execute_actions node\n const dragOverEvent = new CustomEvent('action-drag-over', {\n detail: {\n action: node1.actions[0],\n sourceNodeUuid: 'node-1',\n actionIndex: 0,\n mouseY: 150\n },\n bubbles: false\n });\n routerElement.dispatchEvent(dragOverEvent);\n await routerElement.updateComplete;\n\n // Verify external drag info was NOT stored (rejected due to wrong node type)\n const externalDragInfo = (routerElement as any).externalDragInfo;\n expect(externalDragInfo).to.be.null;\n });\n});\n"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { expect, assert } from '@open-wc/testing';
|
|
2
|
+
import { CanvasMenu } from '../src/flow/CanvasMenu';
|
|
3
|
+
import { assertScreenshot, getClip, getComponent } from './utils.test';
|
|
4
|
+
describe('temba-canvas-menu', () => {
|
|
5
|
+
const createCanvasMenu = async () => {
|
|
6
|
+
const component = (await getComponent('temba-canvas-menu', {}, '', 250, 250));
|
|
7
|
+
await component.updateComplete;
|
|
8
|
+
return component;
|
|
9
|
+
};
|
|
10
|
+
it('can be created', async () => {
|
|
11
|
+
const menu = await createCanvasMenu();
|
|
12
|
+
assert.instanceOf(menu, CanvasMenu);
|
|
13
|
+
expect(menu.open).to.be.false;
|
|
14
|
+
});
|
|
15
|
+
it('is not visible when closed', async () => {
|
|
16
|
+
var _a;
|
|
17
|
+
const menu = await createCanvasMenu();
|
|
18
|
+
expect(menu.open).to.be.false;
|
|
19
|
+
// verify no menu is rendered
|
|
20
|
+
const menuElement = (_a = menu.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.menu');
|
|
21
|
+
expect(menuElement).to.be.null;
|
|
22
|
+
});
|
|
23
|
+
it('shows menu when opened', async () => {
|
|
24
|
+
var _a;
|
|
25
|
+
const menu = await createCanvasMenu();
|
|
26
|
+
// open the menu
|
|
27
|
+
menu.show(100, 100, { x: 50, y: 50 });
|
|
28
|
+
await menu.updateComplete;
|
|
29
|
+
expect(menu.open).to.be.true;
|
|
30
|
+
expect(menu.x).to.equal(100);
|
|
31
|
+
expect(menu.y).to.equal(100);
|
|
32
|
+
// verify menu is rendered
|
|
33
|
+
const menuElement = (_a = menu.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.menu');
|
|
34
|
+
expect(menuElement).to.not.be.null;
|
|
35
|
+
await assertScreenshot('canvas-menu/open', getClip(menu));
|
|
36
|
+
});
|
|
37
|
+
it('has three menu items', async () => {
|
|
38
|
+
var _a;
|
|
39
|
+
const menu = await createCanvasMenu();
|
|
40
|
+
menu.show(100, 100, { x: 50, y: 50 });
|
|
41
|
+
await menu.updateComplete;
|
|
42
|
+
const menuItems = (_a = menu.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.menu-item');
|
|
43
|
+
expect(menuItems === null || menuItems === void 0 ? void 0 : menuItems.length).to.equal(3);
|
|
44
|
+
// check menu item titles
|
|
45
|
+
const titles = Array.from(menuItems || []).map((item) => { var _a; return (_a = item.querySelector('.menu-item-title')) === null || _a === void 0 ? void 0 : _a.textContent; });
|
|
46
|
+
expect(titles).to.deep.equal([
|
|
47
|
+
'Add Action',
|
|
48
|
+
'Add Split',
|
|
49
|
+
'Add Sticky Note'
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
it('closes when close() is called', async () => {
|
|
53
|
+
const menu = await createCanvasMenu();
|
|
54
|
+
menu.show(100, 100, { x: 50, y: 50 });
|
|
55
|
+
await menu.updateComplete;
|
|
56
|
+
expect(menu.open).to.be.true;
|
|
57
|
+
menu.close();
|
|
58
|
+
await menu.updateComplete;
|
|
59
|
+
expect(menu.open).to.be.false;
|
|
60
|
+
});
|
|
61
|
+
it('fires selection event when menu item is clicked', async () => {
|
|
62
|
+
var _a;
|
|
63
|
+
const menu = await createCanvasMenu();
|
|
64
|
+
menu.show(100, 100, { x: 50, y: 50 });
|
|
65
|
+
await menu.updateComplete;
|
|
66
|
+
let selectionFired = false;
|
|
67
|
+
let selectionDetail = null;
|
|
68
|
+
menu.addEventListener('temba-selection', (event) => {
|
|
69
|
+
selectionFired = true;
|
|
70
|
+
selectionDetail = event.detail;
|
|
71
|
+
});
|
|
72
|
+
// click on sticky note option (now the third item)
|
|
73
|
+
const menuItems = (_a = menu.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.menu-item');
|
|
74
|
+
const stickyItem = menuItems === null || menuItems === void 0 ? void 0 : menuItems[2];
|
|
75
|
+
stickyItem.click();
|
|
76
|
+
await menu.updateComplete;
|
|
77
|
+
expect(selectionFired).to.be.true;
|
|
78
|
+
expect(selectionDetail).to.deep.equal({
|
|
79
|
+
action: 'sticky',
|
|
80
|
+
position: { x: 50, y: 50 }
|
|
81
|
+
});
|
|
82
|
+
expect(menu.open).to.be.false;
|
|
83
|
+
});
|
|
84
|
+
it('adjusts position to stay within viewport bounds', async () => {
|
|
85
|
+
var _a, _b;
|
|
86
|
+
const menu = await createCanvasMenu();
|
|
87
|
+
// open menu at position that would go off screen
|
|
88
|
+
const viewportWidth = window.innerWidth;
|
|
89
|
+
const viewportHeight = window.innerHeight;
|
|
90
|
+
const margin = 10; // matches the margin in CanvasMenu
|
|
91
|
+
// position that would go off the right and bottom edges
|
|
92
|
+
menu.show(viewportWidth - 50, viewportHeight - 50, {
|
|
93
|
+
x: 100,
|
|
94
|
+
y: 100
|
|
95
|
+
});
|
|
96
|
+
await menu.updateComplete;
|
|
97
|
+
// wait for position adjustment
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
99
|
+
await menu.updateComplete;
|
|
100
|
+
const menuElement = (_a = menu.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.menu');
|
|
101
|
+
expect(menuElement).to.not.be.null;
|
|
102
|
+
const menuRect = menuElement.getBoundingClientRect();
|
|
103
|
+
// verify menu stays within viewport with margin
|
|
104
|
+
expect(menuRect.right).to.be.at.most(viewportWidth - margin);
|
|
105
|
+
expect(menuRect.bottom).to.be.at.most(viewportHeight - margin);
|
|
106
|
+
// verify click position is preserved (not adjusted)
|
|
107
|
+
let selectionFired = false;
|
|
108
|
+
let selectionDetail = null;
|
|
109
|
+
menu.addEventListener('temba-selection', (event) => {
|
|
110
|
+
selectionFired = true;
|
|
111
|
+
selectionDetail = event.detail;
|
|
112
|
+
});
|
|
113
|
+
const menuItems = (_b = menu.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.menu-item');
|
|
114
|
+
const actionItem = menuItems === null || menuItems === void 0 ? void 0 : menuItems[0];
|
|
115
|
+
actionItem.click();
|
|
116
|
+
await menu.updateComplete;
|
|
117
|
+
expect(selectionFired).to.be.true;
|
|
118
|
+
// click position should remain unchanged
|
|
119
|
+
expect(selectionDetail.position).to.deep.equal({ x: 100, y: 100 });
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=temba-canvas-menu.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temba-canvas-menu.test.js","sourceRoot":"","sources":["../../test/temba-canvas-menu.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, assert } from '@open-wc/testing';\nimport { CanvasMenu } from '../src/flow/CanvasMenu';\nimport { assertScreenshot, getClip, getComponent } from './utils.test';\n\ndescribe('temba-canvas-menu', () => {\n const createCanvasMenu = async () => {\n const component = (await getComponent(\n 'temba-canvas-menu',\n {},\n '',\n 250,\n 250\n )) as CanvasMenu;\n await component.updateComplete;\n return component;\n };\n\n it('can be created', async () => {\n const menu = await createCanvasMenu();\n assert.instanceOf(menu, CanvasMenu);\n expect(menu.open).to.be.false;\n });\n\n it('is not visible when closed', async () => {\n const menu = await createCanvasMenu();\n expect(menu.open).to.be.false;\n\n // verify no menu is rendered\n const menuElement = menu.shadowRoot?.querySelector('.menu');\n expect(menuElement).to.be.null;\n });\n\n it('shows menu when opened', async () => {\n const menu = await createCanvasMenu();\n\n // open the menu\n menu.show(100, 100, { x: 50, y: 50 });\n await menu.updateComplete;\n\n expect(menu.open).to.be.true;\n expect(menu.x).to.equal(100);\n expect(menu.y).to.equal(100);\n\n // verify menu is rendered\n const menuElement = menu.shadowRoot?.querySelector('.menu');\n expect(menuElement).to.not.be.null;\n\n await assertScreenshot('canvas-menu/open', getClip(menu));\n });\n\n it('has three menu items', async () => {\n const menu = await createCanvasMenu();\n menu.show(100, 100, { x: 50, y: 50 });\n await menu.updateComplete;\n\n const menuItems = menu.shadowRoot?.querySelectorAll('.menu-item');\n expect(menuItems?.length).to.equal(3);\n\n // check menu item titles\n const titles = Array.from(menuItems || []).map(\n (item) => item.querySelector('.menu-item-title')?.textContent\n );\n expect(titles).to.deep.equal([\n 'Add Action',\n 'Add Split',\n 'Add Sticky Note'\n ]);\n });\n\n it('closes when close() is called', async () => {\n const menu = await createCanvasMenu();\n menu.show(100, 100, { x: 50, y: 50 });\n await menu.updateComplete;\n\n expect(menu.open).to.be.true;\n\n menu.close();\n await menu.updateComplete;\n\n expect(menu.open).to.be.false;\n });\n\n it('fires selection event when menu item is clicked', async () => {\n const menu = await createCanvasMenu();\n menu.show(100, 100, { x: 50, y: 50 });\n await menu.updateComplete;\n\n let selectionFired = false;\n let selectionDetail = null;\n\n menu.addEventListener('temba-selection', (event: any) => {\n selectionFired = true;\n selectionDetail = event.detail;\n });\n\n // click on sticky note option (now the third item)\n const menuItems = menu.shadowRoot?.querySelectorAll('.menu-item');\n const stickyItem = menuItems?.[2] as HTMLElement;\n stickyItem.click();\n await menu.updateComplete;\n\n expect(selectionFired).to.be.true;\n expect(selectionDetail).to.deep.equal({\n action: 'sticky',\n position: { x: 50, y: 50 }\n });\n expect(menu.open).to.be.false;\n });\n\n it('adjusts position to stay within viewport bounds', async () => {\n const menu = await createCanvasMenu();\n\n // open menu at position that would go off screen\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const margin = 10; // matches the margin in CanvasMenu\n\n // position that would go off the right and bottom edges\n menu.show(viewportWidth - 50, viewportHeight - 50, {\n x: 100,\n y: 100\n });\n await menu.updateComplete;\n\n // wait for position adjustment\n await new Promise((resolve) => setTimeout(resolve, 100));\n await menu.updateComplete;\n\n const menuElement = menu.shadowRoot?.querySelector('.menu') as HTMLElement;\n expect(menuElement).to.not.be.null;\n\n const menuRect = menuElement.getBoundingClientRect();\n\n // verify menu stays within viewport with margin\n expect(menuRect.right).to.be.at.most(viewportWidth - margin);\n expect(menuRect.bottom).to.be.at.most(viewportHeight - margin);\n\n // verify click position is preserved (not adjusted)\n let selectionFired = false;\n let selectionDetail = null;\n\n menu.addEventListener('temba-selection', (event: any) => {\n selectionFired = true;\n selectionDetail = event.detail;\n });\n\n const menuItems = menu.shadowRoot?.querySelectorAll('.menu-item');\n const actionItem = menuItems?.[0] as HTMLElement;\n actionItem.click();\n await menu.updateComplete;\n\n expect(selectionFired).to.be.true;\n // click position should remain unchanged\n expect(selectionDetail.position).to.deep.equal({ x: 100, y: 100 });\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
|
-
|
|
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
|
-
|
|
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,86 @@ 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
|
+
});
|
|
934
1017
|
});
|
|
935
1018
|
//# sourceMappingURL=temba-flow-editor-node.test.js.map
|