@nyaruka/temba-components 0.135.9 → 0.136.1

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 (144) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/demo/components/webchat/example.html +4 -2
  3. package/dist/static/svg/index.svg +1 -1
  4. package/dist/temba-components.js +1351 -322
  5. package/dist/temba-components.js.map +1 -1
  6. package/out-tsc/src/Icons.js +2 -1
  7. package/out-tsc/src/Icons.js.map +1 -1
  8. package/out-tsc/src/display/FloatingTab.js +2 -6
  9. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  10. package/out-tsc/src/flow/CanvasNode.js +29 -1
  11. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  12. package/out-tsc/src/flow/Editor.js +229 -5
  13. package/out-tsc/src/flow/Editor.js.map +1 -1
  14. package/out-tsc/src/flow/Plumber.js +320 -1
  15. package/out-tsc/src/flow/Plumber.js.map +1 -1
  16. package/out-tsc/src/interfaces.js +1 -0
  17. package/out-tsc/src/interfaces.js.map +1 -1
  18. package/out-tsc/src/layout/FloatingWindow.js +30 -8
  19. package/out-tsc/src/layout/FloatingWindow.js.map +1 -1
  20. package/out-tsc/src/simulator/Simulator.js +1861 -0
  21. package/out-tsc/src/simulator/Simulator.js.map +1 -0
  22. package/out-tsc/src/store/AppState.js +66 -0
  23. package/out-tsc/src/store/AppState.js.map +1 -1
  24. package/out-tsc/src/utils.js +48 -0
  25. package/out-tsc/src/utils.js.map +1 -1
  26. package/out-tsc/temba-modules.js +2 -0
  27. package/out-tsc/temba-modules.js.map +1 -1
  28. package/out-tsc/test/temba-appstate-node-sorting.test.js +430 -0
  29. package/out-tsc/test/temba-appstate-node-sorting.test.js.map +1 -0
  30. package/out-tsc/test/temba-floating-tab.test.js +0 -9
  31. package/out-tsc/test/temba-floating-tab.test.js.map +1 -1
  32. package/out-tsc/test/temba-flow-editor.test.js +262 -1
  33. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  34. package/out-tsc/test/temba-flow-plumber-connections.test.js +3 -1
  35. package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
  36. package/out-tsc/test/temba-flow-plumber.test.js +3 -1
  37. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  38. package/out-tsc/test/temba-simulator.test.js +642 -0
  39. package/out-tsc/test/temba-simulator.test.js.map +1 -0
  40. package/out-tsc/test/utils.test.js +1 -1
  41. package/out-tsc/test/utils.test.js.map +1 -1
  42. package/package.json +1 -1
  43. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  44. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  45. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  46. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  47. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  48. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  49. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  50. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  51. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  52. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  53. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  54. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  55. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  56. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  57. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  58. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  59. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  60. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  61. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  62. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  63. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  64. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  65. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  66. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  67. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  68. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  69. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  70. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  71. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  72. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  73. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  74. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  75. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  76. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  77. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  78. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  79. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  80. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  81. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  82. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  83. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  84. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  85. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  86. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  87. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  88. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  89. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  90. package/screenshots/truth/floating-tab/gray.png +0 -0
  91. package/screenshots/truth/floating-tab/green.png +0 -0
  92. package/screenshots/truth/floating-tab/purple.png +0 -0
  93. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  94. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  95. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  96. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  97. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  98. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  99. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  100. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  101. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  102. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  103. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  104. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  105. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  106. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  107. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  108. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  109. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  110. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  111. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  112. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  113. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  114. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  115. package/screenshots/truth/simulator/after-message-sent.png +0 -0
  116. package/screenshots/truth/simulator/after-reset.png +0 -0
  117. package/screenshots/truth/simulator/attachment-menu.png +0 -0
  118. package/screenshots/truth/simulator/context-expanded.png +0 -0
  119. package/screenshots/truth/simulator/context-explorer-open.png +0 -0
  120. package/screenshots/truth/simulator/event-info.png +0 -0
  121. package/screenshots/truth/simulator/image-attachment.png +0 -0
  122. package/screenshots/truth/simulator/open-initial.png +0 -0
  123. package/screenshots/truth/simulator/quick-replies.png +0 -0
  124. package/src/Icons.ts +2 -1
  125. package/src/display/FloatingTab.ts +2 -7
  126. package/src/flow/CanvasNode.ts +30 -1
  127. package/src/flow/Editor.ts +246 -4
  128. package/src/flow/Plumber.ts +371 -2
  129. package/src/interfaces.ts +2 -1
  130. package/src/layout/FloatingWindow.ts +37 -12
  131. package/src/simulator/Simulator.ts +2061 -0
  132. package/src/store/AppState.ts +109 -0
  133. package/src/utils.ts +53 -0
  134. package/static/svg/index.svg +1 -1
  135. package/static/svg/work/traced/route.svg +1 -0
  136. package/static/svg/work/used/route.svg +3 -0
  137. package/temba-modules.ts +2 -0
  138. package/test/temba-appstate-node-sorting.test.ts +506 -0
  139. package/test/temba-floating-tab.test.ts +0 -11
  140. package/test/temba-flow-editor.test.ts +298 -1
  141. package/test/temba-flow-plumber-connections.test.ts +4 -1
  142. package/test/temba-flow-plumber.test.ts +4 -1
  143. package/test/temba-simulator.test.ts +866 -0
  144. package/test/utils.test.ts +1 -1
@@ -0,0 +1,430 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { zustand } from '../src/store/AppState';
3
+ describe('AppState Node Sorting', () => {
4
+ beforeEach(() => {
5
+ // reset the store state before each test
6
+ const state = zustand.getState();
7
+ zustand.setState({
8
+ ...state,
9
+ flowDefinition: {
10
+ language: 'en',
11
+ localization: {},
12
+ name: 'Test Flow',
13
+ nodes: [],
14
+ uuid: 'test-uuid',
15
+ type: 'messaging',
16
+ revision: 1,
17
+ spec_version: '14.3',
18
+ _ui: {
19
+ nodes: {},
20
+ languages: []
21
+ }
22
+ }
23
+ });
24
+ });
25
+ describe('addNode', () => {
26
+ it('should sort nodes by position when adding nodes', () => {
27
+ const state = zustand.getState();
28
+ // add nodes in non-sorted order
29
+ const node1 = {
30
+ uuid: 'node-1',
31
+ actions: [],
32
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
33
+ };
34
+ const nodeUI1 = {
35
+ position: { left: 100, top: 300 }, // middle
36
+ type: 'send_msg'
37
+ };
38
+ const node2 = {
39
+ uuid: 'node-2',
40
+ actions: [],
41
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
42
+ };
43
+ const nodeUI2 = {
44
+ position: { left: 100, top: 100 }, // top
45
+ type: 'send_msg'
46
+ };
47
+ const node3 = {
48
+ uuid: 'node-3',
49
+ actions: [],
50
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
51
+ };
52
+ const nodeUI3 = {
53
+ position: { left: 100, top: 500 }, // bottom
54
+ type: 'send_msg'
55
+ };
56
+ // add in order: middle, top, bottom
57
+ state.addNode(node1, nodeUI1);
58
+ state.addNode(node2, nodeUI2);
59
+ state.addNode(node3, nodeUI3);
60
+ const nodes = zustand.getState().flowDefinition.nodes;
61
+ // nodes should be sorted by y position (top to bottom)
62
+ expect(nodes[0].uuid).to.equal('node-2'); // top: 100
63
+ expect(nodes[1].uuid).to.equal('node-1'); // top: 300
64
+ expect(nodes[2].uuid).to.equal('node-3'); // top: 500
65
+ });
66
+ it('should sort by x when y positions are the same', () => {
67
+ const state = zustand.getState();
68
+ // add nodes with same y but different x
69
+ const node1 = {
70
+ uuid: 'node-1',
71
+ actions: [],
72
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
73
+ };
74
+ const nodeUI1 = {
75
+ position: { left: 300, top: 100 },
76
+ type: 'send_msg'
77
+ };
78
+ const node2 = {
79
+ uuid: 'node-2',
80
+ actions: [],
81
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
82
+ };
83
+ const nodeUI2 = {
84
+ position: { left: 100, top: 100 },
85
+ type: 'send_msg'
86
+ };
87
+ const node3 = {
88
+ uuid: 'node-3',
89
+ actions: [],
90
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
91
+ };
92
+ const nodeUI3 = {
93
+ position: { left: 500, top: 100 },
94
+ type: 'send_msg'
95
+ };
96
+ // add in order: middle, left, right
97
+ state.addNode(node1, nodeUI1);
98
+ state.addNode(node2, nodeUI2);
99
+ state.addNode(node3, nodeUI3);
100
+ const nodes = zustand.getState().flowDefinition.nodes;
101
+ // nodes should be sorted by x position (left to right) since y is same
102
+ expect(nodes[0].uuid).to.equal('node-2'); // left: 100
103
+ expect(nodes[1].uuid).to.equal('node-1'); // left: 300
104
+ expect(nodes[2].uuid).to.equal('node-3'); // left: 500
105
+ });
106
+ it('should handle complex sorting with mixed positions', () => {
107
+ const state = zustand.getState();
108
+ // create a grid of nodes
109
+ // row 1: (100, 100), (200, 100)
110
+ // row 2: (100, 200), (200, 200)
111
+ const nodes = [
112
+ {
113
+ node: {
114
+ uuid: 'node-1',
115
+ actions: [],
116
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
117
+ },
118
+ ui: { position: { left: 200, top: 200 }, type: 'send_msg' }
119
+ },
120
+ {
121
+ node: {
122
+ uuid: 'node-2',
123
+ actions: [],
124
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
125
+ },
126
+ ui: { position: { left: 100, top: 100 }, type: 'send_msg' }
127
+ },
128
+ {
129
+ node: {
130
+ uuid: 'node-3',
131
+ actions: [],
132
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
133
+ },
134
+ ui: { position: { left: 200, top: 100 }, type: 'send_msg' }
135
+ },
136
+ {
137
+ node: {
138
+ uuid: 'node-4',
139
+ actions: [],
140
+ exits: [{ uuid: 'exit-4', destination_uuid: null }]
141
+ },
142
+ ui: { position: { left: 100, top: 200 }, type: 'send_msg' }
143
+ }
144
+ ];
145
+ // add in random order
146
+ nodes.forEach((n) => state.addNode(n.node, n.ui));
147
+ const sortedNodes = zustand.getState().flowDefinition.nodes;
148
+ // expected order: (100,100), (200,100), (100,200), (200,200)
149
+ expect(sortedNodes[0].uuid).to.equal('node-2'); // (100, 100)
150
+ expect(sortedNodes[1].uuid).to.equal('node-3'); // (200, 100)
151
+ expect(sortedNodes[2].uuid).to.equal('node-4'); // (100, 200)
152
+ expect(sortedNodes[3].uuid).to.equal('node-1'); // (200, 200)
153
+ });
154
+ });
155
+ describe('createNode', () => {
156
+ it('should sort nodes after creating a new node', () => {
157
+ const state = zustand.getState();
158
+ // create nodes in non-sorted order
159
+ const uuid1 = state.createNode('send_msg', { left: 300, top: 100 });
160
+ const uuid2 = state.createNode('send_msg', { left: 100, top: 100 });
161
+ const uuid3 = state.createNode('send_msg', { left: 200, top: 100 });
162
+ const nodes = zustand.getState().flowDefinition.nodes;
163
+ // nodes should be sorted by x position
164
+ expect(nodes[0].uuid).to.equal(uuid2); // left: 100
165
+ expect(nodes[1].uuid).to.equal(uuid3); // left: 200
166
+ expect(nodes[2].uuid).to.equal(uuid1); // left: 300
167
+ });
168
+ });
169
+ describe('removeNodes', () => {
170
+ it('should maintain sorting after removing nodes', () => {
171
+ const state = zustand.getState();
172
+ // create nodes
173
+ const node1 = {
174
+ uuid: 'node-1',
175
+ actions: [],
176
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
177
+ };
178
+ const nodeUI1 = {
179
+ position: { left: 100, top: 100 },
180
+ type: 'send_msg'
181
+ };
182
+ const node2 = {
183
+ uuid: 'node-2',
184
+ actions: [],
185
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
186
+ };
187
+ const nodeUI2 = {
188
+ position: { left: 200, top: 100 },
189
+ type: 'send_msg'
190
+ };
191
+ const node3 = {
192
+ uuid: 'node-3',
193
+ actions: [],
194
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
195
+ };
196
+ const nodeUI3 = {
197
+ position: { left: 300, top: 100 },
198
+ type: 'send_msg'
199
+ };
200
+ state.addNode(node1, nodeUI1);
201
+ state.addNode(node2, nodeUI2);
202
+ state.addNode(node3, nodeUI3);
203
+ // remove middle node
204
+ state.removeNodes(['node-2']);
205
+ const nodes = zustand.getState().flowDefinition.nodes;
206
+ // remaining nodes should still be sorted
207
+ expect(nodes.length).to.equal(2);
208
+ expect(nodes[0].uuid).to.equal('node-1'); // left: 100
209
+ expect(nodes[1].uuid).to.equal('node-3'); // left: 300
210
+ });
211
+ it('should sort nodes after connection rerouting during removal', () => {
212
+ const state = zustand.getState();
213
+ // create a chain of nodes
214
+ const node1 = {
215
+ uuid: 'node-1',
216
+ actions: [],
217
+ exits: [{ uuid: 'exit-1', destination_uuid: 'node-2' }]
218
+ };
219
+ const nodeUI1 = {
220
+ position: { left: 100, top: 300 },
221
+ type: 'send_msg'
222
+ };
223
+ const node2 = {
224
+ uuid: 'node-2',
225
+ actions: [],
226
+ exits: [{ uuid: 'exit-2', destination_uuid: 'node-3' }]
227
+ };
228
+ const nodeUI2 = {
229
+ position: { left: 200, top: 200 },
230
+ type: 'send_msg'
231
+ };
232
+ const node3 = {
233
+ uuid: 'node-3',
234
+ actions: [],
235
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
236
+ };
237
+ const nodeUI3 = {
238
+ position: { left: 300, top: 100 },
239
+ type: 'send_msg'
240
+ };
241
+ state.addNode(node1, nodeUI1);
242
+ state.addNode(node2, nodeUI2);
243
+ state.addNode(node3, nodeUI3);
244
+ // verify initial sorting
245
+ let nodes = zustand.getState().flowDefinition.nodes;
246
+ expect(nodes[0].uuid).to.equal('node-3'); // top: 100
247
+ expect(nodes[1].uuid).to.equal('node-2'); // top: 200
248
+ expect(nodes[2].uuid).to.equal('node-1'); // top: 300
249
+ // remove middle node - should reroute connection
250
+ state.removeNodes(['node-2']);
251
+ nodes = zustand.getState().flowDefinition.nodes;
252
+ // nodes should still be sorted
253
+ expect(nodes.length).to.equal(2);
254
+ expect(nodes[0].uuid).to.equal('node-3'); // top: 100
255
+ expect(nodes[1].uuid).to.equal('node-1'); // top: 300
256
+ // verify rerouting happened
257
+ expect(nodes[1].exits[0].destination_uuid).to.equal('node-3');
258
+ });
259
+ });
260
+ describe('updateCanvasPositions', () => {
261
+ it('should re-sort nodes when positions change', () => {
262
+ const state = zustand.getState();
263
+ // create nodes in sorted order
264
+ const node1 = {
265
+ uuid: 'node-1',
266
+ actions: [],
267
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
268
+ };
269
+ const nodeUI1 = {
270
+ position: { left: 100, top: 100 },
271
+ type: 'send_msg'
272
+ };
273
+ const node2 = {
274
+ uuid: 'node-2',
275
+ actions: [],
276
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
277
+ };
278
+ const nodeUI2 = {
279
+ position: { left: 100, top: 200 },
280
+ type: 'send_msg'
281
+ };
282
+ const node3 = {
283
+ uuid: 'node-3',
284
+ actions: [],
285
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
286
+ };
287
+ const nodeUI3 = {
288
+ position: { left: 100, top: 300 },
289
+ type: 'send_msg'
290
+ };
291
+ state.addNode(node1, nodeUI1);
292
+ state.addNode(node2, nodeUI2);
293
+ state.addNode(node3, nodeUI3);
294
+ let nodes = zustand.getState().flowDefinition.nodes;
295
+ expect(nodes[0].uuid).to.equal('node-1'); // top: 100
296
+ expect(nodes[1].uuid).to.equal('node-2'); // top: 200
297
+ expect(nodes[2].uuid).to.equal('node-3'); // top: 300
298
+ // move node-1 to the bottom
299
+ state.updateCanvasPositions({
300
+ 'node-1': { left: 100, top: 400 }
301
+ });
302
+ nodes = zustand.getState().flowDefinition.nodes;
303
+ // nodes should be re-sorted
304
+ expect(nodes[0].uuid).to.equal('node-2'); // top: 200
305
+ expect(nodes[1].uuid).to.equal('node-3'); // top: 300
306
+ expect(nodes[2].uuid).to.equal('node-1'); // top: 400
307
+ });
308
+ it('should handle multiple position updates at once', () => {
309
+ const state = zustand.getState();
310
+ // create nodes
311
+ const node1 = {
312
+ uuid: 'node-1',
313
+ actions: [],
314
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
315
+ };
316
+ const nodeUI1 = {
317
+ position: { left: 100, top: 100 },
318
+ type: 'send_msg'
319
+ };
320
+ const node2 = {
321
+ uuid: 'node-2',
322
+ actions: [],
323
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
324
+ };
325
+ const nodeUI2 = {
326
+ position: { left: 100, top: 200 },
327
+ type: 'send_msg'
328
+ };
329
+ const node3 = {
330
+ uuid: 'node-3',
331
+ actions: [],
332
+ exits: [{ uuid: 'exit-3', destination_uuid: null }]
333
+ };
334
+ const nodeUI3 = {
335
+ position: { left: 100, top: 300 },
336
+ type: 'send_msg'
337
+ };
338
+ state.addNode(node1, nodeUI1);
339
+ state.addNode(node2, nodeUI2);
340
+ state.addNode(node3, nodeUI3);
341
+ // swap positions of node-1 and node-3
342
+ state.updateCanvasPositions({
343
+ 'node-1': { left: 100, top: 300 },
344
+ 'node-3': { left: 100, top: 100 }
345
+ });
346
+ const nodes = zustand.getState().flowDefinition.nodes;
347
+ // nodes should be re-sorted
348
+ expect(nodes[0].uuid).to.equal('node-3'); // top: 100
349
+ expect(nodes[1].uuid).to.equal('node-2'); // top: 200
350
+ expect(nodes[2].uuid).to.equal('node-1'); // top: 300
351
+ });
352
+ it('should not affect sticky notes when updating positions', () => {
353
+ const state = zustand.getState();
354
+ // create a node
355
+ const node = {
356
+ uuid: 'node-1',
357
+ actions: [],
358
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
359
+ };
360
+ const nodeUI = {
361
+ position: { left: 100, top: 100 },
362
+ type: 'send_msg'
363
+ };
364
+ state.addNode(node, nodeUI);
365
+ // create a sticky note
366
+ const stickyUuid = state.createStickyNote({ left: 200, top: 200 });
367
+ // update positions for both
368
+ state.updateCanvasPositions({
369
+ 'node-1': { left: 100, top: 300 },
370
+ [stickyUuid]: { left: 200, top: 100 }
371
+ });
372
+ const flowDef = zustand.getState().flowDefinition;
373
+ // verify node position was updated
374
+ expect(flowDef._ui.nodes['node-1'].position.top).to.equal(300);
375
+ // verify sticky position was updated
376
+ expect(flowDef._ui.stickies[stickyUuid].position.top).to.equal(100);
377
+ });
378
+ });
379
+ describe('edge cases', () => {
380
+ it('should handle nodes with missing position data', () => {
381
+ const state = zustand.getState();
382
+ // manually create a flow definition with a node that has no UI data
383
+ zustand.setState({
384
+ ...zustand.getState(),
385
+ flowDefinition: {
386
+ language: 'en',
387
+ localization: {},
388
+ name: 'Test Flow',
389
+ nodes: [
390
+ {
391
+ uuid: 'node-1',
392
+ actions: [],
393
+ exits: [{ uuid: 'exit-1', destination_uuid: null }]
394
+ }
395
+ ],
396
+ uuid: 'test-uuid',
397
+ type: 'messaging',
398
+ revision: 1,
399
+ spec_version: '14.3',
400
+ _ui: {
401
+ nodes: {}, // no UI data for node-1
402
+ languages: []
403
+ }
404
+ }
405
+ });
406
+ // add a node with position data
407
+ const node2 = {
408
+ uuid: 'node-2',
409
+ actions: [],
410
+ exits: [{ uuid: 'exit-2', destination_uuid: null }]
411
+ };
412
+ const nodeUI2 = {
413
+ position: { left: 100, top: 100 },
414
+ type: 'send_msg'
415
+ };
416
+ // should not throw error
417
+ expect(() => state.addNode(node2, nodeUI2)).to.not.throw();
418
+ const nodes = zustand.getState().flowDefinition.nodes;
419
+ expect(nodes.length).to.equal(2);
420
+ });
421
+ it('should handle empty nodes array', () => {
422
+ const state = zustand.getState();
423
+ // verify initial state is empty
424
+ expect(zustand.getState().flowDefinition.nodes.length).to.equal(0);
425
+ // try to remove nodes from empty flow - should not throw
426
+ expect(() => state.removeNodes(['non-existent'])).to.not.throw();
427
+ });
428
+ });
429
+ });
430
+ //# sourceMappingURL=temba-appstate-node-sorting.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-appstate-node-sorting.test.js","sourceRoot":"","sources":["../../test/temba-appstate-node-sorting.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { zustand } from '../src/store/AppState';\nimport { Node, NodeUI } from '../src/store/flow-definition';\n\ndescribe('AppState Node Sorting', () => {\n beforeEach(() => {\n // reset the store state before each test\n const state = zustand.getState();\n zustand.setState({\n ...state,\n flowDefinition: {\n language: 'en',\n localization: {},\n name: 'Test Flow',\n nodes: [],\n uuid: 'test-uuid',\n type: 'messaging' as const,\n revision: 1,\n spec_version: '14.3',\n _ui: {\n nodes: {},\n languages: []\n }\n }\n });\n });\n\n describe('addNode', () => {\n it('should sort nodes by position when adding nodes', () => {\n const state = zustand.getState();\n\n // add nodes in non-sorted order\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 100, top: 300 }, // middle\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 100, top: 100 }, // top\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 100, top: 500 }, // bottom\n type: 'send_msg'\n };\n\n // add in order: middle, top, bottom\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n const nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should be sorted by y position (top to bottom)\n expect(nodes[0].uuid).to.equal('node-2'); // top: 100\n expect(nodes[1].uuid).to.equal('node-1'); // top: 300\n expect(nodes[2].uuid).to.equal('node-3'); // top: 500\n });\n\n it('should sort by x when y positions are the same', () => {\n const state = zustand.getState();\n\n // add nodes with same y but different x\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 300, top: 100 },\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 500, top: 100 },\n type: 'send_msg'\n };\n\n // add in order: middle, left, right\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n const nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should be sorted by x position (left to right) since y is same\n expect(nodes[0].uuid).to.equal('node-2'); // left: 100\n expect(nodes[1].uuid).to.equal('node-1'); // left: 300\n expect(nodes[2].uuid).to.equal('node-3'); // left: 500\n });\n\n it('should handle complex sorting with mixed positions', () => {\n const state = zustand.getState();\n\n // create a grid of nodes\n // row 1: (100, 100), (200, 100)\n // row 2: (100, 200), (200, 200)\n\n const nodes = [\n {\n node: {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n },\n ui: { position: { left: 200, top: 200 }, type: 'send_msg' as const }\n },\n {\n node: {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n },\n ui: { position: { left: 100, top: 100 }, type: 'send_msg' as const }\n },\n {\n node: {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n },\n ui: { position: { left: 200, top: 100 }, type: 'send_msg' as const }\n },\n {\n node: {\n uuid: 'node-4',\n actions: [],\n exits: [{ uuid: 'exit-4', destination_uuid: null }]\n },\n ui: { position: { left: 100, top: 200 }, type: 'send_msg' as const }\n }\n ];\n\n // add in random order\n nodes.forEach((n) => state.addNode(n.node, n.ui));\n\n const sortedNodes = zustand.getState().flowDefinition.nodes;\n\n // expected order: (100,100), (200,100), (100,200), (200,200)\n expect(sortedNodes[0].uuid).to.equal('node-2'); // (100, 100)\n expect(sortedNodes[1].uuid).to.equal('node-3'); // (200, 100)\n expect(sortedNodes[2].uuid).to.equal('node-4'); // (100, 200)\n expect(sortedNodes[3].uuid).to.equal('node-1'); // (200, 200)\n });\n });\n\n describe('createNode', () => {\n it('should sort nodes after creating a new node', () => {\n const state = zustand.getState();\n\n // create nodes in non-sorted order\n const uuid1 = state.createNode('send_msg', { left: 300, top: 100 });\n const uuid2 = state.createNode('send_msg', { left: 100, top: 100 });\n const uuid3 = state.createNode('send_msg', { left: 200, top: 100 });\n\n const nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should be sorted by x position\n expect(nodes[0].uuid).to.equal(uuid2); // left: 100\n expect(nodes[1].uuid).to.equal(uuid3); // left: 200\n expect(nodes[2].uuid).to.equal(uuid1); // left: 300\n });\n });\n\n describe('removeNodes', () => {\n it('should maintain sorting after removing nodes', () => {\n const state = zustand.getState();\n\n // create nodes\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 200, top: 100 },\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 300, top: 100 },\n type: 'send_msg'\n };\n\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n // remove middle node\n state.removeNodes(['node-2']);\n\n const nodes = zustand.getState().flowDefinition.nodes;\n\n // remaining nodes should still be sorted\n expect(nodes.length).to.equal(2);\n expect(nodes[0].uuid).to.equal('node-1'); // left: 100\n expect(nodes[1].uuid).to.equal('node-3'); // left: 300\n });\n\n it('should sort nodes after connection rerouting during removal', () => {\n const state = zustand.getState();\n\n // create a chain of nodes\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: 'node-2' }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 100, top: 300 },\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: 'node-3' }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 200, top: 200 },\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 300, top: 100 },\n type: 'send_msg'\n };\n\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n // verify initial sorting\n let nodes = zustand.getState().flowDefinition.nodes;\n expect(nodes[0].uuid).to.equal('node-3'); // top: 100\n expect(nodes[1].uuid).to.equal('node-2'); // top: 200\n expect(nodes[2].uuid).to.equal('node-1'); // top: 300\n\n // remove middle node - should reroute connection\n state.removeNodes(['node-2']);\n\n nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should still be sorted\n expect(nodes.length).to.equal(2);\n expect(nodes[0].uuid).to.equal('node-3'); // top: 100\n expect(nodes[1].uuid).to.equal('node-1'); // top: 300\n\n // verify rerouting happened\n expect(nodes[1].exits[0].destination_uuid).to.equal('node-3');\n });\n });\n\n describe('updateCanvasPositions', () => {\n it('should re-sort nodes when positions change', () => {\n const state = zustand.getState();\n\n // create nodes in sorted order\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 100, top: 200 },\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 100, top: 300 },\n type: 'send_msg'\n };\n\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n let nodes = zustand.getState().flowDefinition.nodes;\n expect(nodes[0].uuid).to.equal('node-1'); // top: 100\n expect(nodes[1].uuid).to.equal('node-2'); // top: 200\n expect(nodes[2].uuid).to.equal('node-3'); // top: 300\n\n // move node-1 to the bottom\n state.updateCanvasPositions({\n 'node-1': { left: 100, top: 400 }\n });\n\n nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should be re-sorted\n expect(nodes[0].uuid).to.equal('node-2'); // top: 200\n expect(nodes[1].uuid).to.equal('node-3'); // top: 300\n expect(nodes[2].uuid).to.equal('node-1'); // top: 400\n });\n\n it('should handle multiple position updates at once', () => {\n const state = zustand.getState();\n\n // create nodes\n const node1: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI1: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 100, top: 200 },\n type: 'send_msg'\n };\n\n const node3: Node = {\n uuid: 'node-3',\n actions: [],\n exits: [{ uuid: 'exit-3', destination_uuid: null }]\n };\n const nodeUI3: NodeUI = {\n position: { left: 100, top: 300 },\n type: 'send_msg'\n };\n\n state.addNode(node1, nodeUI1);\n state.addNode(node2, nodeUI2);\n state.addNode(node3, nodeUI3);\n\n // swap positions of node-1 and node-3\n state.updateCanvasPositions({\n 'node-1': { left: 100, top: 300 },\n 'node-3': { left: 100, top: 100 }\n });\n\n const nodes = zustand.getState().flowDefinition.nodes;\n\n // nodes should be re-sorted\n expect(nodes[0].uuid).to.equal('node-3'); // top: 100\n expect(nodes[1].uuid).to.equal('node-2'); // top: 200\n expect(nodes[2].uuid).to.equal('node-1'); // top: 300\n });\n\n it('should not affect sticky notes when updating positions', () => {\n const state = zustand.getState();\n\n // create a node\n const node: Node = {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n };\n const nodeUI: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n state.addNode(node, nodeUI);\n\n // create a sticky note\n const stickyUuid = state.createStickyNote({ left: 200, top: 200 });\n\n // update positions for both\n state.updateCanvasPositions({\n 'node-1': { left: 100, top: 300 },\n [stickyUuid]: { left: 200, top: 100 }\n });\n\n const flowDef = zustand.getState().flowDefinition;\n\n // verify node position was updated\n expect(flowDef._ui.nodes['node-1'].position.top).to.equal(300);\n\n // verify sticky position was updated\n expect(flowDef._ui.stickies[stickyUuid].position.top).to.equal(100);\n });\n });\n\n describe('edge cases', () => {\n it('should handle nodes with missing position data', () => {\n const state = zustand.getState();\n\n // manually create a flow definition with a node that has no UI data\n zustand.setState({\n ...zustand.getState(),\n flowDefinition: {\n language: 'en',\n localization: {},\n name: 'Test Flow',\n nodes: [\n {\n uuid: 'node-1',\n actions: [],\n exits: [{ uuid: 'exit-1', destination_uuid: null }]\n }\n ],\n uuid: 'test-uuid',\n type: 'messaging' as const,\n revision: 1,\n spec_version: '14.3',\n _ui: {\n nodes: {}, // no UI data for node-1\n languages: []\n }\n }\n });\n\n // add a node with position data\n const node2: Node = {\n uuid: 'node-2',\n actions: [],\n exits: [{ uuid: 'exit-2', destination_uuid: null }]\n };\n const nodeUI2: NodeUI = {\n position: { left: 100, top: 100 },\n type: 'send_msg'\n };\n\n // should not throw error\n expect(() => state.addNode(node2, nodeUI2)).to.not.throw();\n\n const nodes = zustand.getState().flowDefinition.nodes;\n expect(nodes.length).to.equal(2);\n });\n\n it('should handle empty nodes array', () => {\n const state = zustand.getState();\n\n // verify initial state is empty\n expect(zustand.getState().flowDefinition.nodes.length).to.equal(0);\n\n // try to remove nodes from empty flow - should not throw\n expect(() => state.removeNodes(['non-existent'])).to.not.throw();\n });\n });\n});\n"]}
@@ -78,14 +78,5 @@ describe('temba-floating-tab', () => {
78
78
  await assertScreenshot('floating-tab/gray', getClip(tab2));
79
79
  await assertScreenshot('floating-tab/purple', getClip(tab3));
80
80
  });
81
- it('supports custom positioning', async () => {
82
- const tab = (await getComponent('temba-floating-tab', {
83
- icon: 'phone',
84
- label: 'Phone Simulator',
85
- color: '#10b981',
86
- top: 250
87
- }));
88
- expect(tab.top).to.equal(250);
89
- });
90
81
  });
91
82
  //# sourceMappingURL=temba-floating-tab.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"temba-floating-tab.test.js","sourceRoot":"","sources":["../../test/temba-floating-tab.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, assert } from '@open-wc/testing';\nimport { FloatingTab } from '../src/display/FloatingTab';\nimport { assertScreenshot, getClip, getComponent } from './utils.test';\n\ndescribe('temba-floating-tab', () => {\n it('can be created', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#10b981',\n top: 100\n })) as FloatingTab;\n\n assert.instanceOf(tab, FloatingTab);\n expect(tab.icon).to.equal('phone');\n expect(tab.label).to.equal('Phone Simulator');\n expect(tab.color).to.equal('#10b981');\n expect(tab.top).to.equal(100);\n expect(tab.hidden).to.equal(false);\n\n await assertScreenshot('floating-tab/default', getClip(tab));\n });\n\n it('can be hidden', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#10b981',\n hidden: true\n })) as FloatingTab;\n\n expect(tab.hidden).to.equal(true);\n expect(tab.classList.contains('hidden')).to.equal(true);\n\n await assertScreenshot('floating-tab/hidden', getClip(tab));\n });\n\n it('shows label on hover', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#6366f1'\n })) as FloatingTab;\n\n const tabElement = tab.shadowRoot.querySelector('.tab') as HTMLElement;\n expect(tabElement).to.exist;\n\n // simulate hover state\n const labelElement = tab.shadowRoot.querySelector('.label') as HTMLElement;\n expect(labelElement).to.exist;\n\n await assertScreenshot('floating-tab/hover', getClip(tab));\n });\n\n it('fires click event', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'clock',\n label: 'History',\n color: '#8b5cf6'\n })) as FloatingTab;\n\n let clicked = false;\n tab.addEventListener('temba-button-clicked', () => {\n clicked = true;\n });\n\n const tabElement = tab.shadowRoot.querySelector('.tab') as HTMLElement;\n tabElement.click();\n\n expect(clicked).to.equal(true);\n });\n\n it('supports different colors', async () => {\n const tab1 = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone',\n color: '#10b981',\n top: 100\n })) as FloatingTab;\n\n const tab2 = (await getComponent('temba-floating-tab', {\n icon: 'globe',\n label: 'Translation',\n color: '#6b7280',\n top: 200\n })) as FloatingTab;\n\n const tab3 = (await getComponent('temba-floating-tab', {\n icon: 'clock',\n label: 'History',\n color: '#8b5cf6',\n top: 300\n })) as FloatingTab;\n\n await assertScreenshot('floating-tab/green', getClip(tab1));\n await assertScreenshot('floating-tab/gray', getClip(tab2));\n await assertScreenshot('floating-tab/purple', getClip(tab3));\n });\n\n it('supports custom positioning', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#10b981',\n top: 250\n })) as FloatingTab;\n\n expect(tab.top).to.equal(250);\n });\n});\n"]}
1
+ {"version":3,"file":"temba-floating-tab.test.js","sourceRoot":"","sources":["../../test/temba-floating-tab.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, assert } from '@open-wc/testing';\nimport { FloatingTab } from '../src/display/FloatingTab';\nimport { assertScreenshot, getClip, getComponent } from './utils.test';\n\ndescribe('temba-floating-tab', () => {\n it('can be created', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#10b981',\n top: 100\n })) as FloatingTab;\n\n assert.instanceOf(tab, FloatingTab);\n expect(tab.icon).to.equal('phone');\n expect(tab.label).to.equal('Phone Simulator');\n expect(tab.color).to.equal('#10b981');\n expect(tab.top).to.equal(100);\n expect(tab.hidden).to.equal(false);\n\n await assertScreenshot('floating-tab/default', getClip(tab));\n });\n\n it('can be hidden', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#10b981',\n hidden: true\n })) as FloatingTab;\n\n expect(tab.hidden).to.equal(true);\n expect(tab.classList.contains('hidden')).to.equal(true);\n\n await assertScreenshot('floating-tab/hidden', getClip(tab));\n });\n\n it('shows label on hover', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone Simulator',\n color: '#6366f1'\n })) as FloatingTab;\n\n const tabElement = tab.shadowRoot.querySelector('.tab') as HTMLElement;\n expect(tabElement).to.exist;\n\n // simulate hover state\n const labelElement = tab.shadowRoot.querySelector('.label') as HTMLElement;\n expect(labelElement).to.exist;\n\n await assertScreenshot('floating-tab/hover', getClip(tab));\n });\n\n it('fires click event', async () => {\n const tab = (await getComponent('temba-floating-tab', {\n icon: 'clock',\n label: 'History',\n color: '#8b5cf6'\n })) as FloatingTab;\n\n let clicked = false;\n tab.addEventListener('temba-button-clicked', () => {\n clicked = true;\n });\n\n const tabElement = tab.shadowRoot.querySelector('.tab') as HTMLElement;\n tabElement.click();\n\n expect(clicked).to.equal(true);\n });\n\n it('supports different colors', async () => {\n const tab1 = (await getComponent('temba-floating-tab', {\n icon: 'phone',\n label: 'Phone',\n color: '#10b981',\n top: 100\n })) as FloatingTab;\n\n const tab2 = (await getComponent('temba-floating-tab', {\n icon: 'globe',\n label: 'Translation',\n color: '#6b7280',\n top: 200\n })) as FloatingTab;\n\n const tab3 = (await getComponent('temba-floating-tab', {\n icon: 'clock',\n label: 'History',\n color: '#8b5cf6',\n top: 300\n })) as FloatingTab;\n\n await assertScreenshot('floating-tab/green', getClip(tab1));\n await assertScreenshot('floating-tab/gray', getClip(tab2));\n await assertScreenshot('floating-tab/purple', getClip(tab3));\n });\n});\n"]}