@nyaruka/temba-components 0.129.7 → 0.129.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/.devcontainer/Dockerfile +11 -4
  2. package/.devcontainer/devcontainer.json +3 -2
  3. package/.github/workflows/build.yml +4 -14
  4. package/CHANGELOG.md +8 -3
  5. package/demo/components/flow/example.html +1 -1
  6. package/demo/components/message-editor/example.html +125 -0
  7. package/demo/components/textinput/completion.html +1 -0
  8. package/demo/data/flows/food-order.json +12 -21
  9. package/demo/data/flows/sample-flow.json +42 -26
  10. package/dist/temba-components.js +506 -218
  11. package/dist/temba-components.js.map +1 -1
  12. package/out-tsc/src/display/Thumbnail.js +2 -1
  13. package/out-tsc/src/display/Thumbnail.js.map +1 -1
  14. package/out-tsc/src/events.js.map +1 -1
  15. package/out-tsc/src/flow/NodeEditor.js +245 -22
  16. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  17. package/out-tsc/src/flow/actions/call_webhook.js +26 -17
  18. package/out-tsc/src/flow/actions/call_webhook.js.map +1 -1
  19. package/out-tsc/src/flow/actions/send_msg.js +147 -6
  20. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  21. package/out-tsc/src/flow/types.js.map +1 -1
  22. package/out-tsc/src/form/ArrayEditor.js +111 -38
  23. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  24. package/out-tsc/src/form/BaseListEditor.js +19 -4
  25. package/out-tsc/src/form/BaseListEditor.js.map +1 -1
  26. package/out-tsc/src/form/FormField.js +1 -1
  27. package/out-tsc/src/form/FormField.js.map +1 -1
  28. package/out-tsc/src/form/KeyValueEditor.js +1 -1
  29. package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
  30. package/out-tsc/src/form/MediaPicker.js +13 -1
  31. package/out-tsc/src/form/MediaPicker.js.map +1 -1
  32. package/out-tsc/src/form/MessageEditor.js +422 -0
  33. package/out-tsc/src/form/MessageEditor.js.map +1 -0
  34. package/out-tsc/src/form/TextInput.js +12 -5
  35. package/out-tsc/src/form/TextInput.js.map +1 -1
  36. package/out-tsc/src/form/select/Select.js +4 -4
  37. package/out-tsc/src/form/select/Select.js.map +1 -1
  38. package/out-tsc/src/live/ContactChat.js +27 -2
  39. package/out-tsc/src/live/ContactChat.js.map +1 -1
  40. package/out-tsc/temba-modules.js +2 -0
  41. package/out-tsc/temba-modules.js.map +1 -1
  42. package/out-tsc/test/temba-field-config.test.js +4 -2
  43. package/out-tsc/test/temba-field-config.test.js.map +1 -1
  44. package/out-tsc/test/temba-message-editor.test.js +194 -0
  45. package/out-tsc/test/temba-message-editor.test.js.map +1 -0
  46. package/out-tsc/test/temba-node-editor.test.js +71 -0
  47. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  48. package/out-tsc/test/temba-select.test.js +1 -1
  49. package/out-tsc/test/temba-select.test.js.map +1 -1
  50. package/out-tsc/test/temba-textinput.test.js +16 -0
  51. package/out-tsc/test/temba-textinput.test.js.map +1 -1
  52. package/out-tsc/test/temba-webchat.test.js +4 -0
  53. package/out-tsc/test/temba-webchat.test.js.map +1 -1
  54. package/out-tsc/test/utils.test.js +2 -8
  55. package/out-tsc/test/utils.test.js.map +1 -1
  56. package/package.json +7 -4
  57. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  58. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  59. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  60. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  61. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  62. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  63. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  64. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  65. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  66. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  67. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  68. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  69. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  70. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  71. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  72. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  73. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  74. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  75. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  76. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  77. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  78. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  79. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  80. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  81. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  82. package/screenshots/truth/editor/send_msg.png +0 -0
  83. package/screenshots/truth/editor/set_contact_language.png +0 -0
  84. package/screenshots/truth/editor/set_contact_name.png +0 -0
  85. package/screenshots/truth/editor/set_run_result.png +0 -0
  86. package/screenshots/truth/formfield/markdown-errors.png +0 -0
  87. package/screenshots/truth/formfield/no-errors.png +0 -0
  88. package/screenshots/truth/formfield/plain-text-errors.png +0 -0
  89. package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
  90. package/screenshots/truth/message-editor/default.png +0 -0
  91. package/screenshots/truth/message-editor/drag-highlight.png +0 -0
  92. package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
  93. package/screenshots/truth/message-editor/with-completion.png +0 -0
  94. package/screenshots/truth/message-editor/with-properties.png +0 -0
  95. package/screenshots/truth/textinput/autogrow-initial.png +0 -0
  96. package/screenshots/truth/textinput/input-form.png +0 -0
  97. package/src/display/Thumbnail.ts +2 -1
  98. package/src/events.ts +5 -0
  99. package/src/flow/NodeEditor.ts +269 -23
  100. package/src/flow/actions/call_webhook.ts +28 -18
  101. package/src/flow/actions/send_msg.ts +170 -6
  102. package/src/flow/types.ts +21 -2
  103. package/src/form/ArrayEditor.ts +120 -42
  104. package/src/form/BaseListEditor.ts +22 -6
  105. package/src/form/FormField.ts +1 -1
  106. package/src/form/KeyValueEditor.ts +1 -1
  107. package/src/form/MediaPicker.ts +13 -1
  108. package/src/form/MessageEditor.ts +449 -0
  109. package/src/form/TextInput.ts +15 -7
  110. package/src/form/select/Select.ts +4 -4
  111. package/src/live/ContactChat.ts +30 -4
  112. package/static/css/temba-components.css +2 -0
  113. package/static/mr/docs/en-us/editor.json +2588 -0
  114. package/stress-test.js +138 -0
  115. package/temba-modules.ts +2 -0
  116. package/test/temba-field-config.test.ts +4 -2
  117. package/test/temba-message-editor.test.ts +300 -0
  118. package/test/temba-node-editor.test.ts +94 -0
  119. package/test/temba-select.test.ts +1 -1
  120. package/test/temba-textinput.test.ts +26 -0
  121. package/test/temba-webchat.test.ts +5 -0
  122. package/test/utils.test.ts +2 -13
  123. package/test-assets/contacts/history.json +19 -0
  124. package/test-assets/style.css +2 -0
  125. package/web-dev-mock.mjs +433 -0
  126. package/web-dev-server.config.mjs +51 -5
  127. package/web-test-runner.config.mjs +9 -4
@@ -0,0 +1,194 @@
1
+ import { fixture, assert, expect } from '@open-wc/testing';
2
+ import { MessageEditor } from '../src/form/MessageEditor';
3
+ import { assertScreenshot, getClip, getComponent } from './utils.test';
4
+ export const getHTML = (options = {}) => {
5
+ const attrs = Object.keys(options)
6
+ .map((key) => `${key}="${options[key]}"`)
7
+ .join(' ');
8
+ return `<temba-message-editor ${attrs}></temba-message-editor>`;
9
+ };
10
+ describe('temba-message-editor', () => {
11
+ it('can be created', async () => {
12
+ const editor = await fixture(getHTML());
13
+ assert.instanceOf(editor, MessageEditor);
14
+ });
15
+ it('has default properties', async () => {
16
+ const editor = (await getComponent('temba-message-editor'));
17
+ expect(editor.name).to.equal('');
18
+ expect(editor.value).to.equal('');
19
+ expect(editor.placeholder).to.equal('');
20
+ expect(editor.textarea).to.be.true;
21
+ expect(editor.autogrow).to.be.true;
22
+ expect(editor.minHeight).to.equal(60);
23
+ expect(editor.attachments).to.deep.equal([]);
24
+ expect(editor.maxAttachments).to.equal(3);
25
+ await assertScreenshot('message-editor/default', getClip(editor));
26
+ });
27
+ it('can set properties', async () => {
28
+ const editor = (await getComponent('temba-message-editor', {
29
+ name: 'message',
30
+ value: 'Hello world',
31
+ placeholder: 'Type your message...',
32
+ 'max-attachments': '5'
33
+ }));
34
+ expect(editor.name).to.equal('message');
35
+ expect(editor.value).to.equal('Hello world');
36
+ expect(editor.placeholder).to.equal('Type your message...');
37
+ expect(editor.maxAttachments).to.equal(5);
38
+ await assertScreenshot('message-editor/with-properties', getClip(editor));
39
+ });
40
+ it('renders completion component', async () => {
41
+ const editor = (await getComponent('temba-message-editor', {
42
+ value: 'Test message',
43
+ placeholder: 'Enter message'
44
+ }));
45
+ const completion = editor.shadowRoot.querySelector('temba-completion');
46
+ expect(completion).to.not.be.null;
47
+ expect(completion.hasAttribute('widgetOnly')).to.be.true;
48
+ await assertScreenshot('message-editor/with-completion', getClip(editor));
49
+ });
50
+ it('filters runtime attachments', async () => {
51
+ const attachments = [
52
+ 'image/jpeg:http://example.com/image.jpg',
53
+ 'image:@fields.profile_pic',
54
+ 'video:@fields.intro_video',
55
+ 'application/pdf:http://example.com/doc.pdf'
56
+ ];
57
+ const editor = (await getComponent('temba-message-editor', {
58
+ attachments: JSON.stringify(attachments)
59
+ }));
60
+ // Wait for component to update
61
+ await editor.updateComplete;
62
+ const mediaPicker = editor.shadowRoot.querySelector('temba-media-picker');
63
+ expect(mediaPicker).to.not.be.null;
64
+ // Should only have the static attachments (those with '/' in content type)
65
+ expect(mediaPicker.attachments.length).to.equal(2);
66
+ expect(mediaPicker.attachments[0].content_type).to.equal('image/jpeg');
67
+ expect(mediaPicker.attachments[1].content_type).to.equal('application/pdf');
68
+ await assertScreenshot('message-editor/filtered-attachments', getClip(editor));
69
+ });
70
+ it('handles completion change events', async () => {
71
+ const editor = (await getComponent('temba-message-editor'));
72
+ let changeEvent = null;
73
+ editor.addEventListener('change', (e) => {
74
+ changeEvent = e;
75
+ });
76
+ const completion = editor.shadowRoot.querySelector('temba-completion');
77
+ completion.value = 'New message';
78
+ completion.dispatchEvent(new Event('change'));
79
+ expect(editor.value).to.equal('New message');
80
+ expect(changeEvent).to.not.be.null;
81
+ });
82
+ it('handles media picker change events', async () => {
83
+ const editor = (await getComponent('temba-message-editor'));
84
+ let changeEvent = null;
85
+ editor.addEventListener('change', (e) => {
86
+ changeEvent = e;
87
+ });
88
+ const mediaPicker = editor.shadowRoot.querySelector('temba-media-picker');
89
+ mediaPicker.attachments = [
90
+ {
91
+ content_type: 'image/jpeg',
92
+ url: 'http://example.com/test.jpg',
93
+ filename: 'test.jpg',
94
+ size: 1024
95
+ }
96
+ ];
97
+ mediaPicker.dispatchEvent(new Event('change'));
98
+ expect(editor.attachments).to.include('image/jpeg:http://example.com/test.jpg');
99
+ expect(changeEvent).to.not.be.null;
100
+ });
101
+ it('preserves runtime attachments when media changes', async () => {
102
+ const initialAttachments = [
103
+ 'image:@fields.profile_pic',
104
+ 'image/jpeg:http://example.com/existing.jpg'
105
+ ];
106
+ const editor = (await getComponent('temba-message-editor', {
107
+ attachments: JSON.stringify(initialAttachments)
108
+ }));
109
+ await editor.updateComplete;
110
+ // Simulate media picker change
111
+ const mediaPicker = editor.shadowRoot.querySelector('temba-media-picker');
112
+ mediaPicker.attachments = [
113
+ {
114
+ content_type: 'image/png',
115
+ url: 'http://example.com/new.png',
116
+ filename: 'new.png',
117
+ size: 2048
118
+ }
119
+ ];
120
+ mediaPicker.dispatchEvent(new Event('change'));
121
+ // Should preserve runtime attachments and add new static ones
122
+ expect(editor.attachments).to.include('image:@fields.profile_pic');
123
+ expect(editor.attachments).to.include('image/png:http://example.com/new.png');
124
+ expect(editor.attachments.length).to.equal(2);
125
+ });
126
+ it('supports drag and drop highlighting', async () => {
127
+ const editor = (await getComponent('temba-message-editor'));
128
+ const container = editor.shadowRoot.querySelector('.message-editor-container');
129
+ // Simulate drag enter
130
+ const dragEvent = new DragEvent('dragenter', {
131
+ bubbles: true,
132
+ dataTransfer: new DataTransfer()
133
+ });
134
+ container.dispatchEvent(dragEvent);
135
+ // Wait for the update
136
+ await editor.updateComplete;
137
+ expect(editor.pendingDrop).to.be.true;
138
+ expect(container.classList.contains('highlight')).to.be.true;
139
+ await assertScreenshot('message-editor/drag-highlight', getClip(editor));
140
+ // Simulate drag leave
141
+ const dragLeaveEvent = new DragEvent('dragleave', {
142
+ bubbles: true,
143
+ dataTransfer: new DataTransfer()
144
+ });
145
+ container.dispatchEvent(dragLeaveEvent);
146
+ expect(editor.pendingDrop).to.be.false;
147
+ });
148
+ it('focuses completion on focus', async () => {
149
+ const editor = (await getComponent('temba-message-editor'));
150
+ const completion = editor.shadowRoot.querySelector('temba-completion');
151
+ let focusCalled = false;
152
+ completion.focus = () => {
153
+ focusCalled = true;
154
+ };
155
+ editor.focus();
156
+ expect(focusCalled).to.be.true;
157
+ });
158
+ it('clicks completion on click', async () => {
159
+ const editor = (await getComponent('temba-message-editor'));
160
+ const completion = editor.shadowRoot.querySelector('temba-completion');
161
+ let clickCalled = false;
162
+ completion.click = () => {
163
+ clickCalled = true;
164
+ };
165
+ editor.click();
166
+ expect(clickCalled).to.be.true;
167
+ });
168
+ it('initializes with correct height for long text content', async () => {
169
+ const longText = 'This is a very long text that should span multiple lines and cause the autogrow functionality to kick in and expand the textarea to accommodate all the content. This text should be long enough to trigger the autogrow behavior during initialization.';
170
+ const editor = (await getComponent('temba-message-editor', {
171
+ value: longText,
172
+ 'min-height': '60'
173
+ }));
174
+ // Wait for component to fully render
175
+ await editor.updateComplete;
176
+ // Get the text input element to verify its height
177
+ const completion = editor.shadowRoot.querySelector('temba-completion');
178
+ expect(completion).to.not.be.null;
179
+ // The completion should have the long text value
180
+ expect(completion.value).to.equal(longText);
181
+ // Get the actual TextInput component inside the completion
182
+ const textInput = completion.getTextInput();
183
+ expect(textInput).to.not.be.null;
184
+ // The textarea should be in autogrow mode
185
+ expect(textInput.autogrow).to.be.true;
186
+ expect(textInput.textarea).to.be.true;
187
+ // Check that the autogrow div has been updated with content
188
+ const autogrowDiv = textInput.shadowRoot.querySelector('.grow-wrap > div');
189
+ expect(autogrowDiv).to.not.be.null;
190
+ expect(autogrowDiv.innerText).to.include(longText);
191
+ await assertScreenshot('message-editor/autogrow-initial-content', getClip(editor));
192
+ });
193
+ });
194
+ //# sourceMappingURL=temba-message-editor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-message-editor.test.js","sourceRoot":"","sources":["../../test/temba-message-editor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, assert, expect } from '@open-wc/testing';\nimport { MessageEditor } from '../src/form/MessageEditor';\nimport { assertScreenshot, getClip, getComponent } from './utils.test';\n\nexport const getHTML = (options: any = {}) => {\n const attrs = Object.keys(options)\n .map((key) => `${key}=\"${options[key]}\"`)\n .join(' ');\n return `<temba-message-editor ${attrs}></temba-message-editor>`;\n};\n\ndescribe('temba-message-editor', () => {\n it('can be created', async () => {\n const editor: MessageEditor = await fixture(getHTML());\n assert.instanceOf(editor, MessageEditor);\n });\n\n it('has default properties', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n\n expect(editor.name).to.equal('');\n expect(editor.value).to.equal('');\n expect(editor.placeholder).to.equal('');\n expect(editor.textarea).to.be.true;\n expect(editor.autogrow).to.be.true;\n expect(editor.minHeight).to.equal(60);\n expect(editor.attachments).to.deep.equal([]);\n expect(editor.maxAttachments).to.equal(3);\n\n await assertScreenshot(\n 'message-editor/default',\n getClip(editor as HTMLElement)\n );\n });\n\n it('can set properties', async () => {\n const editor = (await getComponent('temba-message-editor', {\n name: 'message',\n value: 'Hello world',\n placeholder: 'Type your message...',\n 'max-attachments': '5'\n })) as MessageEditor;\n\n expect(editor.name).to.equal('message');\n expect(editor.value).to.equal('Hello world');\n expect(editor.placeholder).to.equal('Type your message...');\n expect(editor.maxAttachments).to.equal(5);\n\n await assertScreenshot(\n 'message-editor/with-properties',\n getClip(editor as HTMLElement)\n );\n });\n\n it('renders completion component', async () => {\n const editor = (await getComponent('temba-message-editor', {\n value: 'Test message',\n placeholder: 'Enter message'\n })) as MessageEditor;\n\n const completion = editor.shadowRoot.querySelector(\n 'temba-completion'\n ) as any;\n expect(completion).to.not.be.null;\n expect(completion.hasAttribute('widgetOnly')).to.be.true;\n\n await assertScreenshot(\n 'message-editor/with-completion',\n getClip(editor as HTMLElement)\n );\n });\n\n it('filters runtime attachments', async () => {\n const attachments = [\n 'image/jpeg:http://example.com/image.jpg',\n 'image:@fields.profile_pic',\n 'video:@fields.intro_video',\n 'application/pdf:http://example.com/doc.pdf'\n ];\n\n const editor = (await getComponent('temba-message-editor', {\n attachments: JSON.stringify(attachments)\n })) as MessageEditor;\n\n // Wait for component to update\n await editor.updateComplete;\n\n const mediaPicker = editor.shadowRoot.querySelector(\n 'temba-media-picker'\n ) as any;\n expect(mediaPicker).to.not.be.null;\n\n // Should only have the static attachments (those with '/' in content type)\n expect(mediaPicker.attachments.length).to.equal(2);\n expect(mediaPicker.attachments[0].content_type).to.equal('image/jpeg');\n expect(mediaPicker.attachments[1].content_type).to.equal('application/pdf');\n\n await assertScreenshot(\n 'message-editor/filtered-attachments',\n getClip(editor as HTMLElement)\n );\n });\n\n it('handles completion change events', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n let changeEvent: CustomEvent = null;\n\n editor.addEventListener('change', (e: CustomEvent) => {\n changeEvent = e;\n });\n\n const completion = editor.shadowRoot.querySelector(\n 'temba-completion'\n ) as any;\n completion.value = 'New message';\n completion.dispatchEvent(new Event('change'));\n\n expect(editor.value).to.equal('New message');\n expect(changeEvent).to.not.be.null;\n });\n\n it('handles media picker change events', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n let changeEvent: CustomEvent = null;\n\n editor.addEventListener('change', (e: CustomEvent) => {\n changeEvent = e;\n });\n\n const mediaPicker = editor.shadowRoot.querySelector(\n 'temba-media-picker'\n ) as any;\n mediaPicker.attachments = [\n {\n content_type: 'image/jpeg',\n url: 'http://example.com/test.jpg',\n filename: 'test.jpg',\n size: 1024\n }\n ];\n mediaPicker.dispatchEvent(new Event('change'));\n\n expect(editor.attachments).to.include(\n 'image/jpeg:http://example.com/test.jpg'\n );\n expect(changeEvent).to.not.be.null;\n });\n\n it('preserves runtime attachments when media changes', async () => {\n const initialAttachments = [\n 'image:@fields.profile_pic',\n 'image/jpeg:http://example.com/existing.jpg'\n ];\n\n const editor = (await getComponent('temba-message-editor', {\n attachments: JSON.stringify(initialAttachments)\n })) as MessageEditor;\n\n await editor.updateComplete;\n\n // Simulate media picker change\n const mediaPicker = editor.shadowRoot.querySelector(\n 'temba-media-picker'\n ) as any;\n mediaPicker.attachments = [\n {\n content_type: 'image/png',\n url: 'http://example.com/new.png',\n filename: 'new.png',\n size: 2048\n }\n ];\n mediaPicker.dispatchEvent(new Event('change'));\n\n // Should preserve runtime attachments and add new static ones\n expect(editor.attachments).to.include('image:@fields.profile_pic');\n expect(editor.attachments).to.include(\n 'image/png:http://example.com/new.png'\n );\n expect(editor.attachments.length).to.equal(2);\n });\n\n it('supports drag and drop highlighting', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n const container = editor.shadowRoot.querySelector(\n '.message-editor-container'\n );\n\n // Simulate drag enter\n const dragEvent = new DragEvent('dragenter', {\n bubbles: true,\n dataTransfer: new DataTransfer()\n });\n container.dispatchEvent(dragEvent);\n\n // Wait for the update\n await editor.updateComplete;\n\n expect(editor.pendingDrop).to.be.true;\n expect(container.classList.contains('highlight')).to.be.true;\n\n await assertScreenshot(\n 'message-editor/drag-highlight',\n getClip(editor as HTMLElement)\n );\n\n // Simulate drag leave\n const dragLeaveEvent = new DragEvent('dragleave', {\n bubbles: true,\n dataTransfer: new DataTransfer()\n });\n container.dispatchEvent(dragLeaveEvent);\n\n expect(editor.pendingDrop).to.be.false;\n });\n\n it('focuses completion on focus', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n const completion = editor.shadowRoot.querySelector(\n 'temba-completion'\n ) as any;\n\n let focusCalled = false;\n completion.focus = () => {\n focusCalled = true;\n };\n\n editor.focus();\n expect(focusCalled).to.be.true;\n });\n\n it('clicks completion on click', async () => {\n const editor = (await getComponent(\n 'temba-message-editor'\n )) as MessageEditor;\n const completion = editor.shadowRoot.querySelector(\n 'temba-completion'\n ) as any;\n\n let clickCalled = false;\n completion.click = () => {\n clickCalled = true;\n };\n\n editor.click();\n expect(clickCalled).to.be.true;\n });\n\n it('initializes with correct height for long text content', async () => {\n const longText =\n 'This is a very long text that should span multiple lines and cause the autogrow functionality to kick in and expand the textarea to accommodate all the content. This text should be long enough to trigger the autogrow behavior during initialization.';\n\n const editor = (await getComponent('temba-message-editor', {\n value: longText,\n 'min-height': '60'\n })) as MessageEditor;\n\n // Wait for component to fully render\n await editor.updateComplete;\n\n // Get the text input element to verify its height\n const completion = editor.shadowRoot.querySelector(\n 'temba-completion'\n ) as any;\n expect(completion).to.not.be.null;\n\n // The completion should have the long text value\n expect(completion.value).to.equal(longText);\n\n // Get the actual TextInput component inside the completion\n const textInput = completion.getTextInput();\n expect(textInput).to.not.be.null;\n\n // The textarea should be in autogrow mode\n expect(textInput.autogrow).to.be.true;\n expect(textInput.textarea).to.be.true;\n\n // Check that the autogrow div has been updated with content\n const autogrowDiv = textInput.shadowRoot.querySelector(\n '.grow-wrap > div'\n ) as HTMLDivElement;\n expect(autogrowDiv).to.not.be.null;\n expect(autogrowDiv.innerText).to.include(longText);\n\n await assertScreenshot(\n 'message-editor/autogrow-initial-content',\n getClip(editor as HTMLElement)\n );\n });\n});\n"]}
@@ -29,6 +29,28 @@ describe('temba-node-editor', () => {
29
29
  expect(el.shadowRoot).to.not.be.null;
30
30
  expect(el.action).to.equal(action);
31
31
  });
32
+ it('renders send_msg action with message editor', async () => {
33
+ const action = {
34
+ uuid: 'test-action-uuid',
35
+ type: 'send_msg',
36
+ text: 'Hello @contact.name, check this out!',
37
+ attachments: [
38
+ 'image/jpeg:http://example.com/photo.jpg',
39
+ 'image:@fields.profile_pic'
40
+ ],
41
+ quick_replies: ['Yes', 'No']
42
+ };
43
+ const el = (await fixture(html `
44
+ <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>
45
+ `));
46
+ await el.updateComplete;
47
+ expect(el.shadowRoot).to.not.be.null;
48
+ expect(el.action).to.equal(action);
49
+ // Check that the message editor component is rendered
50
+ const messageEditor = el.shadowRoot.querySelector('temba-message-editor');
51
+ expect(messageEditor).to.not.be.null;
52
+ expect(messageEditor.value).to.equal(action.text);
53
+ });
32
54
  it('renders set_run_result action', async () => {
33
55
  const action = {
34
56
  uuid: 'test-action-uuid',
@@ -279,5 +301,54 @@ describe('temba-node-editor', () => {
279
301
  await assertDialogScreenshot(el, `editor/${actionType.type}`);
280
302
  }
281
303
  });
304
+ it('displays bubble count for group value counts', async () => {
305
+ const action = {
306
+ uuid: 'test-action-uuid',
307
+ type: 'send_msg',
308
+ text: 'Hello world',
309
+ quick_replies: ['Yes', 'No', 'Maybe'],
310
+ attachments: ['image:@contact.photo', 'document:@contact.resume']
311
+ };
312
+ const el = (await fixture(html `
313
+ <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>
314
+ `));
315
+ await el.updateComplete;
316
+ // Wait for form data to be fully initialized and re-render to complete
317
+ await new Promise((resolve) => setTimeout(resolve, 200));
318
+ await el.updateComplete;
319
+ // Check that bubble counts are displayed
320
+ const shadowRoot = el.shadowRoot;
321
+ const bubbles = shadowRoot.querySelectorAll('.group-count-bubble');
322
+ // Should have bubbles for groups with values
323
+ expect(bubbles.length).to.be.greaterThan(0);
324
+ // Check specific bubble values (trim to handle whitespace in rendered text)
325
+ const bubbleTexts = Array.from(bubbles).map((bubble) => { var _a; return (_a = bubble.textContent) === null || _a === void 0 ? void 0 : _a.trim(); });
326
+ // Runtime attachments group should show bubble when collapsed and has values
327
+ expect(bubbleTexts).to.include('2'); // 2 runtime attachments
328
+ // Note: Quick replies group auto-expands when it has content, so no bubble is shown
329
+ });
330
+ it('shows arrow when group has no values', async () => {
331
+ const action = {
332
+ uuid: 'test-action-uuid',
333
+ type: 'send_msg',
334
+ text: 'Hello world'
335
+ // No quick_replies or attachments provided
336
+ };
337
+ const el = (await fixture(html `
338
+ <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>
339
+ `));
340
+ await el.updateComplete;
341
+ // Wait for form data initialization
342
+ await new Promise((resolve) => setTimeout(resolve, 200));
343
+ await el.updateComplete;
344
+ // Check that arrows are displayed instead of bubbles
345
+ const shadowRoot = el.shadowRoot;
346
+ const bubbles = shadowRoot.querySelectorAll('.group-count-bubble');
347
+ const arrows = shadowRoot.querySelectorAll('.group-toggle-icon');
348
+ // Should have no bubbles when counts are 0
349
+ expect(bubbles.length).to.equal(0);
350
+ // Should have arrows for collapsible groups
351
+ expect(arrows.length).to.be.greaterThan(0);
352
+ });
282
353
  });
283
354
  //# sourceMappingURL=temba-node-editor.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"temba-node-editor.test.js","sourceRoot":"","sources":["../../test/temba-node-editor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import '../temba-modules';\nimport { html, fixture, expect } from '@open-wc/testing';\nimport { assertScreenshot, getClip } from './utils.test';\n\n// Define interface for NodeEditor component\ninterface NodeEditorElement extends HTMLElement {\n action?: any;\n node?: any;\n nodeUI?: any;\n isOpen?: boolean;\n updateComplete: Promise<boolean>;\n}\n\nconst assertDialogScreenshot = async (\n el: NodeEditorElement,\n screenshotName: string\n) => {\n const dialog = el.shadowRoot\n .querySelector('temba-dialog')\n .shadowRoot.querySelector('.dialog-container') as HTMLElement;\n await assertScreenshot(screenshotName, getClip(dialog));\n};\n\ndescribe('temba-node-editor', () => {\n it('can be created', async () => {\n const el = (await fixture(html`\n <temba-node-editor .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n expect(el).to.exist;\n expect(el.tagName).to.equal('TEMBA-NODE-EDITOR');\n });\n\n it('renders send_msg action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders set_run_result action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'set_run_result',\n name: 'result_name',\n value: 'result_value'\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders set_contact_field action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'set_contact_field',\n field: { key: 'age', name: 'Age' },\n value: '25'\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders add_contact_groups action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'add_contact_groups',\n groups: [{ uuid: 'group-1', name: 'Test Group' }]\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders enter_flow action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'enter_flow',\n flow: { uuid: 'flow-1', name: 'Sub Flow' }\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders node with router configuration', async () => {\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: [{ uuid: 'exit-1', name: 'Default' }],\n router: {\n type: 'switch',\n result_name: 'result',\n categories: [{ uuid: 'cat-1', name: 'Category 1', exit_uuid: 'exit-1' }]\n }\n };\n\n const nodeUI = {\n type: 'split_by_expression',\n position: { left: 100, top: 100 }\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .node=${node}\n .nodeUI=${nodeUI}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.node).to.equal(node);\n expect(el.nodeUI).to.equal(nodeUI);\n\n await assertDialogScreenshot(el, 'editor/router');\n });\n\n it('renders node with wait configuration', async () => {\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: [{ uuid: 'exit-1', name: 'Default' }],\n wait: {\n type: 'msg'\n }\n };\n\n const nodeUI = {\n type: 'wait_for_response',\n position: { left: 100, top: 100 }\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .node=${node}\n .nodeUI=${nodeUI}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.node).to.equal(node);\n expect(el.nodeUI).to.equal(nodeUI);\n\n await assertDialogScreenshot(el, 'editor/wait');\n });\n\n it('handles different button actions', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n let saveEventFired = false;\n let cancelEventFired = false;\n\n el.addEventListener('temba-action-saved', () => {\n saveEventFired = true;\n });\n\n el.addEventListener('temba-node-edit-cancelled', () => {\n cancelEventFired = true;\n });\n\n // Get the dialog element inside the node editor\n const dialog = el.shadowRoot!.querySelector('temba-dialog');\n expect(dialog).to.not.be.null;\n\n // Test Save button by dispatching event on the dialog\n const saveEvent = new CustomEvent('temba-button-clicked', {\n detail: { button: { name: 'Save' } },\n bubbles: true\n });\n dialog!.dispatchEvent(saveEvent);\n expect(saveEventFired).to.equal(true);\n\n // Reset for cancel test\n saveEventFired = false;\n\n // Test Cancel button\n const cancelEvent = new CustomEvent('temba-button-clicked', {\n detail: { button: { name: 'Cancel' } },\n bubbles: true\n });\n dialog!.dispatchEvent(cancelEvent);\n expect(cancelEventFired).to.equal(true);\n });\n\n it('handles property updates', async () => {\n const el = (await fixture(html`\n <temba-node-editor .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n // Test action property update\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n el.action = action;\n await el.updateComplete;\n expect(el.action).to.equal(action);\n\n // Test node property update\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: []\n };\n\n el.node = node;\n await el.updateComplete;\n expect(el.node).to.equal(node);\n\n // Test nodeUI property update\n const nodeUI = {\n type: 'execute_actions',\n position: { left: 100, top: 100 }\n };\n\n el.nodeUI = nodeUI;\n await el.updateComplete;\n expect(el.nodeUI).to.equal(nodeUI);\n });\n\n it('handles form submission events', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Since the form submission handling is complex and involves internal components,\n // we'll just verify the component renders without errors and has the expected structure\n const shadowRoot = el.shadowRoot;\n expect(shadowRoot).to.not.be.null;\n\n // Verify dialog is present\n const dialog = shadowRoot!.querySelector('temba-dialog');\n expect(dialog).to.not.be.null;\n });\n\n it('handles form validation', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Test that the component renders form elements\n const shadowRoot = el.shadowRoot;\n expect(shadowRoot).to.not.be.null;\n });\n\n it('renders different action types correctly', async () => {\n const actionTypes = [\n {\n type: 'send_msg',\n data: { text: 'Message', quick_replies: [] }\n },\n {\n type: 'set_run_result',\n data: { name: 'result', value: 'value' }\n },\n {\n type: 'set_contact_name',\n data: { name: 'John Doe' }\n },\n {\n type: 'set_contact_language',\n data: { language: 'eng' }\n }\n ];\n\n for (const actionType of actionTypes) {\n const action = {\n uuid: `test-${actionType.type}`,\n type: actionType.type,\n ...actionType.data\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .action=${action}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action.type).to.equal(actionType.type);\n\n await assertDialogScreenshot(el, `editor/${actionType.type}`);\n }\n });\n});\n"]}
1
+ {"version":3,"file":"temba-node-editor.test.js","sourceRoot":"","sources":["../../test/temba-node-editor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import '../temba-modules';\nimport { html, fixture, expect } from '@open-wc/testing';\nimport { assertScreenshot, getClip } from './utils.test';\n\n// Define interface for NodeEditor component\ninterface NodeEditorElement extends HTMLElement {\n action?: any;\n node?: any;\n nodeUI?: any;\n isOpen?: boolean;\n updateComplete: Promise<boolean>;\n}\n\nconst assertDialogScreenshot = async (\n el: NodeEditorElement,\n screenshotName: string\n) => {\n const dialog = el.shadowRoot\n .querySelector('temba-dialog')\n .shadowRoot.querySelector('.dialog-container') as HTMLElement;\n await assertScreenshot(screenshotName, getClip(dialog));\n};\n\ndescribe('temba-node-editor', () => {\n it('can be created', async () => {\n const el = (await fixture(html`\n <temba-node-editor .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n expect(el).to.exist;\n expect(el.tagName).to.equal('TEMBA-NODE-EDITOR');\n });\n\n it('renders send_msg action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders send_msg action with message editor', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello @contact.name, check this out!',\n attachments: [\n 'image/jpeg:http://example.com/photo.jpg',\n 'image:@fields.profile_pic'\n ],\n quick_replies: ['Yes', 'No']\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n\n // Check that the message editor component is rendered\n const messageEditor = el.shadowRoot.querySelector(\n 'temba-message-editor'\n ) as any;\n expect(messageEditor).to.not.be.null;\n expect(messageEditor.value).to.equal(action.text);\n });\n\n it('renders set_run_result action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'set_run_result',\n name: 'result_name',\n value: 'result_value'\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders set_contact_field action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'set_contact_field',\n field: { key: 'age', name: 'Age' },\n value: '25'\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders add_contact_groups action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'add_contact_groups',\n groups: [{ uuid: 'group-1', name: 'Test Group' }]\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders enter_flow action', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'enter_flow',\n flow: { uuid: 'flow-1', name: 'Sub Flow' }\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action).to.equal(action);\n });\n\n it('renders node with router configuration', async () => {\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: [{ uuid: 'exit-1', name: 'Default' }],\n router: {\n type: 'switch',\n result_name: 'result',\n categories: [{ uuid: 'cat-1', name: 'Category 1', exit_uuid: 'exit-1' }]\n }\n };\n\n const nodeUI = {\n type: 'split_by_expression',\n position: { left: 100, top: 100 }\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .node=${node}\n .nodeUI=${nodeUI}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.node).to.equal(node);\n expect(el.nodeUI).to.equal(nodeUI);\n\n await assertDialogScreenshot(el, 'editor/router');\n });\n\n it('renders node with wait configuration', async () => {\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: [{ uuid: 'exit-1', name: 'Default' }],\n wait: {\n type: 'msg'\n }\n };\n\n const nodeUI = {\n type: 'wait_for_response',\n position: { left: 100, top: 100 }\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .node=${node}\n .nodeUI=${nodeUI}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.node).to.equal(node);\n expect(el.nodeUI).to.equal(nodeUI);\n\n await assertDialogScreenshot(el, 'editor/wait');\n });\n\n it('handles different button actions', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n let saveEventFired = false;\n let cancelEventFired = false;\n\n el.addEventListener('temba-action-saved', () => {\n saveEventFired = true;\n });\n\n el.addEventListener('temba-node-edit-cancelled', () => {\n cancelEventFired = true;\n });\n\n // Get the dialog element inside the node editor\n const dialog = el.shadowRoot!.querySelector('temba-dialog');\n expect(dialog).to.not.be.null;\n\n // Test Save button by dispatching event on the dialog\n const saveEvent = new CustomEvent('temba-button-clicked', {\n detail: { button: { name: 'Save' } },\n bubbles: true\n });\n dialog!.dispatchEvent(saveEvent);\n expect(saveEventFired).to.equal(true);\n\n // Reset for cancel test\n saveEventFired = false;\n\n // Test Cancel button\n const cancelEvent = new CustomEvent('temba-button-clicked', {\n detail: { button: { name: 'Cancel' } },\n bubbles: true\n });\n dialog!.dispatchEvent(cancelEvent);\n expect(cancelEventFired).to.equal(true);\n });\n\n it('handles property updates', async () => {\n const el = (await fixture(html`\n <temba-node-editor .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n // Test action property update\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n el.action = action;\n await el.updateComplete;\n expect(el.action).to.equal(action);\n\n // Test node property update\n const node = {\n uuid: 'test-node-uuid',\n actions: [],\n exits: []\n };\n\n el.node = node;\n await el.updateComplete;\n expect(el.node).to.equal(node);\n\n // Test nodeUI property update\n const nodeUI = {\n type: 'execute_actions',\n position: { left: 100, top: 100 }\n };\n\n el.nodeUI = nodeUI;\n await el.updateComplete;\n expect(el.nodeUI).to.equal(nodeUI);\n });\n\n it('handles form submission events', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Since the form submission handling is complex and involves internal components,\n // we'll just verify the component renders without errors and has the expected structure\n const shadowRoot = el.shadowRoot;\n expect(shadowRoot).to.not.be.null;\n\n // Verify dialog is present\n const dialog = shadowRoot!.querySelector('temba-dialog');\n expect(dialog).to.not.be.null;\n });\n\n it('handles form validation', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: []\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Test that the component renders form elements\n const shadowRoot = el.shadowRoot;\n expect(shadowRoot).to.not.be.null;\n });\n\n it('renders different action types correctly', async () => {\n const actionTypes = [\n {\n type: 'send_msg',\n data: { text: 'Message', quick_replies: [] }\n },\n {\n type: 'set_run_result',\n data: { name: 'result', value: 'value' }\n },\n {\n type: 'set_contact_name',\n data: { name: 'John Doe' }\n },\n {\n type: 'set_contact_language',\n data: { language: 'eng' }\n }\n ];\n\n for (const actionType of actionTypes) {\n const action = {\n uuid: `test-${actionType.type}`,\n type: actionType.type,\n ...actionType.data\n };\n\n const el = (await fixture(html`\n <temba-node-editor\n .action=${action}\n .isOpen=${true}\n ></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n expect(el.shadowRoot).to.not.be.null;\n expect(el.action.type).to.equal(actionType.type);\n\n await assertDialogScreenshot(el, `editor/${actionType.type}`);\n }\n });\n\n it('displays bubble count for group value counts', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world',\n quick_replies: ['Yes', 'No', 'Maybe'],\n attachments: ['image:@contact.photo', 'document:@contact.resume']\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Wait for form data to be fully initialized and re-render to complete\n await new Promise((resolve) => setTimeout(resolve, 200));\n await el.updateComplete;\n\n // Check that bubble counts are displayed\n const shadowRoot = el.shadowRoot;\n const bubbles = shadowRoot.querySelectorAll('.group-count-bubble');\n\n // Should have bubbles for groups with values\n expect(bubbles.length).to.be.greaterThan(0);\n\n // Check specific bubble values (trim to handle whitespace in rendered text)\n const bubbleTexts = Array.from(bubbles).map((bubble) =>\n bubble.textContent?.trim()\n );\n\n // Runtime attachments group should show bubble when collapsed and has values\n expect(bubbleTexts).to.include('2'); // 2 runtime attachments\n // Note: Quick replies group auto-expands when it has content, so no bubble is shown\n });\n\n it('shows arrow when group has no values', async () => {\n const action = {\n uuid: 'test-action-uuid',\n type: 'send_msg',\n text: 'Hello world'\n // No quick_replies or attachments provided\n };\n\n const el = (await fixture(html`\n <temba-node-editor .action=${action} .isOpen=${true}></temba-node-editor>\n `)) as NodeEditorElement;\n\n await el.updateComplete;\n\n // Wait for form data initialization\n await new Promise((resolve) => setTimeout(resolve, 200));\n await el.updateComplete;\n\n // Check that arrows are displayed instead of bubbles\n const shadowRoot = el.shadowRoot;\n const bubbles = shadowRoot.querySelectorAll('.group-count-bubble');\n const arrows = shadowRoot.querySelectorAll('.group-toggle-icon');\n\n // Should have no bubbles when counts are 0\n expect(bubbles.length).to.equal(0);\n\n // Should have arrows for collapsible groups\n expect(arrows.length).to.be.greaterThan(0);\n });\n});\n"]}
@@ -627,7 +627,7 @@ describe('temba-select', () => {
627
627
  await select.updateComplete;
628
628
  await openSelect(clock, select);
629
629
  // Cached results should be available immediately, but give some time for rendering
630
- await waitForSelectPagination(select, clock, 15, 30);
630
+ await waitForSelectPagination(select, clock, 15, 10);
631
631
  assert.equal(select.visibleOptions.length, 15);
632
632
  // close and reopen once more (previous bug failed on third opening)
633
633
  // select.blur();