@nyaruka/temba-components 0.130.0 → 0.130.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +34 -4
  2. package/DEV_DATA.md +89 -0
  3. package/demo/data/flows/food-order.json +4 -4
  4. package/demo/data/flows/sample-flow.json +132 -147
  5. package/dist/temba-components.js +764 -628
  6. package/dist/temba-components.js.map +1 -1
  7. package/out-tsc/src/display/Chat.js +5 -3
  8. package/out-tsc/src/display/Chat.js.map +1 -1
  9. package/out-tsc/src/events.js.map +1 -1
  10. package/out-tsc/src/flow/CanvasNode.js +83 -78
  11. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  12. package/out-tsc/src/flow/Editor.js +1 -0
  13. package/out-tsc/src/flow/Editor.js.map +1 -1
  14. package/out-tsc/src/flow/NodeEditor.js +47 -3
  15. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  16. package/out-tsc/src/flow/actions/add_contact_groups.js +13 -1
  17. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  18. package/out-tsc/src/flow/actions/add_contact_urn.js +1 -1
  19. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  20. package/out-tsc/src/flow/actions/add_input_labels.js +1 -0
  21. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  22. package/out-tsc/src/flow/actions/set_contact_channel.js +1 -1
  23. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  24. package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
  25. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  26. package/out-tsc/src/flow/actions/set_contact_language.js +3 -1
  27. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  28. package/out-tsc/src/flow/actions/set_contact_name.js +1 -1
  29. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  30. package/out-tsc/src/flow/actions/set_contact_status.js +17 -14
  31. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  32. package/out-tsc/src/flow/actions/set_run_result.js +1 -1
  33. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  34. package/out-tsc/src/flow/nodes/split_by_llm.js +12 -12
  35. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  36. package/out-tsc/src/flow/nodes/wait_for_response.js +609 -6
  37. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  38. package/out-tsc/src/flow/operators.js +194 -0
  39. package/out-tsc/src/flow/operators.js.map +1 -0
  40. package/out-tsc/src/flow/types.js.map +1 -1
  41. package/out-tsc/src/form/ArrayEditor.js +84 -19
  42. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  43. package/out-tsc/src/form/Checkbox.js +12 -0
  44. package/out-tsc/src/form/Checkbox.js.map +1 -1
  45. package/out-tsc/src/form/FieldRenderer.js +13 -3
  46. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  47. package/out-tsc/src/form/TextInput.js +20 -1
  48. package/out-tsc/src/form/TextInput.js.map +1 -1
  49. package/out-tsc/src/form/select/Select.js +14 -1
  50. package/out-tsc/src/form/select/Select.js.map +1 -1
  51. package/out-tsc/src/interfaces.js.map +1 -1
  52. package/out-tsc/src/layout/Dialog.js +3 -4
  53. package/out-tsc/src/layout/Dialog.js.map +1 -1
  54. package/out-tsc/src/list/RunList.js +2 -2
  55. package/out-tsc/src/list/RunList.js.map +1 -1
  56. package/out-tsc/src/live/ContactChat.js +114 -34
  57. package/out-tsc/src/live/ContactChat.js.map +1 -1
  58. package/out-tsc/src/live/ContactDetails.js +7 -0
  59. package/out-tsc/src/live/ContactDetails.js.map +1 -1
  60. package/out-tsc/src/live/ContactNameFetch.js +1 -1
  61. package/out-tsc/src/live/ContactNameFetch.js.map +1 -1
  62. package/out-tsc/test/NodeHelper.js +25 -27
  63. package/out-tsc/test/NodeHelper.js.map +1 -1
  64. package/out-tsc/test/nodes/split_by_llm.test.js +12 -4
  65. package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -1
  66. package/out-tsc/test/nodes/split_by_llm_categorize.test.js +101 -91
  67. package/out-tsc/test/nodes/split_by_llm_categorize.test.js.map +1 -1
  68. package/out-tsc/test/nodes/split_by_random.test.js +120 -112
  69. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  70. package/out-tsc/test/nodes/wait_for_digits.test.js +131 -111
  71. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  72. package/out-tsc/test/nodes/wait_for_response.test.js +549 -85
  73. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  74. package/out-tsc/test/temba-checkbox.test.js +32 -32
  75. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  76. package/out-tsc/test/temba-contact-chat.test.js +2 -1
  77. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  78. package/out-tsc/test/temba-dropdown.test.js +0 -4
  79. package/out-tsc/test/temba-dropdown.test.js.map +1 -1
  80. package/out-tsc/test/temba-flow-editor-node.test.js +9 -4
  81. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  82. package/out-tsc/test/temba-integration-markdown.test.js +13 -15
  83. package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
  84. package/out-tsc/test/temba-node-editor.test.js +5 -38
  85. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  86. package/out-tsc/test/temba-run-list.test.js +2 -2
  87. package/out-tsc/test/temba-run-list.test.js.map +1 -1
  88. package/out-tsc/test/utils.test.js +2 -1
  89. package/out-tsc/test/utils.test.js.map +1 -1
  90. package/package.json +6 -2
  91. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  92. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  93. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  94. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  95. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  96. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  97. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  98. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  99. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  100. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  101. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  102. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  103. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  104. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  105. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  106. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  107. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  108. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  109. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  110. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  111. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  112. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  113. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  114. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  115. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  116. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  117. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  118. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  119. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  120. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  121. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  122. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  123. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  124. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  125. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  126. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  127. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  128. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  129. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  130. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  131. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  132. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  133. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  134. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  135. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  136. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  137. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  138. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  139. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  140. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  141. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  142. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  143. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  144. package/screenshots/truth/checkbox/checkbox-whitespace-label-no-background-hover.png +0 -0
  145. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  146. package/screenshots/truth/checkbox/checked.png +0 -0
  147. package/screenshots/truth/checkbox/default.png +0 -0
  148. package/screenshots/truth/editor/wait.png +0 -0
  149. package/screenshots/truth/integration/textinput-markdown-errors.png +0 -0
  150. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  151. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  152. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  153. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  154. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  155. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  156. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  157. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  158. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  159. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  160. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  161. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  162. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  163. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  164. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  165. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  166. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  167. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  168. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  169. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  170. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  171. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  172. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  173. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  174. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  175. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  176. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  177. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  178. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  179. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  180. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  181. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  182. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  183. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  184. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  185. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  186. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  187. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  188. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  189. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  190. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  191. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  192. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  193. package/screenshots/truth/run-list/basic.png +0 -0
  194. package/screenshots/truth/templates/default.png +0 -0
  195. package/screenshots/truth/wait-for-response/rules-editor.png +0 -0
  196. package/screenshots/truth/wait-for-response/timeout-editor-unchecked.png +0 -0
  197. package/screenshots/truth/wait-for-response/timeout-editor.png +0 -0
  198. package/scripts/dev-data-sync.mjs +182 -0
  199. package/src/display/Chat.ts +6 -4
  200. package/src/events.ts +6 -5
  201. package/src/flow/CanvasNode.ts +89 -79
  202. package/src/flow/Editor.ts +1 -0
  203. package/src/flow/NodeEditor.ts +55 -3
  204. package/src/flow/actions/add_contact_groups.ts +16 -1
  205. package/src/flow/actions/add_contact_urn.ts +1 -1
  206. package/src/flow/actions/add_input_labels.ts +1 -0
  207. package/src/flow/actions/set_contact_channel.ts +1 -1
  208. package/src/flow/actions/set_contact_field.ts +2 -1
  209. package/src/flow/actions/set_contact_language.ts +3 -1
  210. package/src/flow/actions/set_contact_name.ts +1 -1
  211. package/src/flow/actions/set_contact_status.ts +18 -18
  212. package/src/flow/actions/set_run_result.ts +1 -1
  213. package/src/flow/nodes/split_by_llm.ts +14 -13
  214. package/src/flow/nodes/wait_for_response.ts +717 -5
  215. package/src/flow/operators.ts +215 -0
  216. package/src/flow/types.ts +10 -2
  217. package/src/form/ArrayEditor.ts +117 -37
  218. package/src/form/Checkbox.ts +12 -0
  219. package/src/form/FieldRenderer.ts +24 -3
  220. package/src/form/TextInput.ts +19 -1
  221. package/src/form/select/Select.ts +15 -4
  222. package/src/interfaces.ts +1 -1
  223. package/src/layout/Dialog.ts +4 -4
  224. package/src/list/RunList.ts +2 -2
  225. package/src/live/ContactChat.ts +144 -58
  226. package/src/live/ContactDetails.ts +7 -0
  227. package/src/live/ContactNameFetch.ts +1 -1
  228. package/static/api/labels.json +6 -1
  229. package/test/NodeHelper.ts +38 -40
  230. package/test/nodes/split_by_llm.test.ts +43 -32
  231. package/test/nodes/split_by_llm_categorize.test.ts +130 -120
  232. package/test/nodes/split_by_random.test.ts +136 -128
  233. package/test/nodes/wait_for_digits.test.ts +147 -127
  234. package/test/nodes/wait_for_response.test.ts +657 -104
  235. package/test/temba-checkbox.test.ts +36 -32
  236. package/test/temba-contact-chat.test.ts +2 -1
  237. package/test/temba-dropdown.test.ts +0 -12
  238. package/test/temba-flow-editor-node.test.ts +11 -4
  239. package/test/temba-integration-markdown.test.ts +16 -17
  240. package/test/temba-node-editor.test.ts +5 -43
  241. package/test/temba-run-list.test.ts +2 -2
  242. package/test/utils.test.ts +2 -1
  243. package/test-assets/list/runs.json +8 -8
  244. package/web-dev-mock.mjs +86 -30
  245. package/web-dev-server.config.mjs +272 -31
  246. package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
  247. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  248. package/screenshots/truth/editor/send_msg.png +0 -0
  249. package/screenshots/truth/editor/set_contact_language.png +0 -0
  250. package/screenshots/truth/editor/set_contact_name.png +0 -0
  251. package/screenshots/truth/editor/set_run_result.png +0 -0
  252. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
@@ -1,24 +1,26 @@
1
1
  import { html, fixture, expect } from '@open-wc/testing';
2
2
  import { Checkbox } from '../src/form/Checkbox';
3
- import { assertScreenshot, getClip } from './utils.test';
3
+ import { assertScreenshot, getClip, getComponent } from './utils.test';
4
+
5
+ const getCheckbox = async (props: any) => {
6
+ return (await getComponent('temba-checkbox', props)) as Checkbox;
7
+ };
4
8
 
5
9
  describe('temba-checkbox', () => {
6
10
  it('renders default checkbox', async () => {
7
- const el: Checkbox = await fixture(html`
8
- <temba-checkbox label="My Checkbox"></temba-checkbox>
9
- `);
11
+ const el = await getCheckbox({
12
+ label: 'My Checkbox'
13
+ });
10
14
 
11
15
  expect(el.label).to.equal('My Checkbox');
12
16
  await assertScreenshot('checkbox/default', getClip(el));
13
17
  });
14
18
 
15
19
  it('can select by clicking on the label', async () => {
16
- const el: Checkbox = await fixture(html`
17
- <temba-checkbox
18
- label="My Checkbox"
19
- animatechange="false"
20
- ></temba-checkbox>
21
- `);
20
+ const el: Checkbox = await getCheckbox({
21
+ label: 'My Checkbox',
22
+ animatechange: false
23
+ });
22
24
 
23
25
  (el.shadowRoot.querySelector('.checkbox-label') as HTMLDivElement).click();
24
26
  expect(el.checked).to.equal(true);
@@ -28,9 +30,9 @@ describe('temba-checkbox', () => {
28
30
  it('fires change event on click', async () => {
29
31
  // eslint-disable-next-line no-async-promise-executor
30
32
  return new Promise<void>(async (resolve) => {
31
- const checkbox: Checkbox = await fixture(html`
32
- <temba-checkbox label="My Checkbox"></temba-checkbox>
33
- `);
33
+ const checkbox = await getCheckbox({
34
+ label: 'My Checkbox'
35
+ });
34
36
 
35
37
  checkbox.addEventListener('change', () => {
36
38
  resolve();
@@ -41,17 +43,19 @@ describe('temba-checkbox', () => {
41
43
  });
42
44
 
43
45
  it('checks via click method', async () => {
44
- const checkbox: Checkbox = await fixture(html`
45
- <temba-checkbox label="My Checkbox"></temba-checkbox>
46
- `);
46
+ const checkbox = await getCheckbox({
47
+ label: 'My Checkbox'
48
+ });
47
49
  checkbox.click();
48
50
  expect(checkbox.checked).to.equal(true);
49
51
  });
50
52
 
51
53
  it('has background hover effect when label is set', async () => {
52
- const el: Checkbox = await fixture(html`
53
- <temba-checkbox name="My Checkbox" label="My Label"></temba-checkbox>
54
- `);
54
+ const el: Checkbox = await getCheckbox({
55
+ label: 'My Label',
56
+ name: 'My Checkbox'
57
+ });
58
+
55
59
  expect(el.label).to.equal('My Label');
56
60
  //the ".wrapper.label" style results in the background hover effect
57
61
  const wrapperDivEl = el.shadowRoot.querySelector(
@@ -68,9 +72,10 @@ describe('temba-checkbox', () => {
68
72
  //but this is the expected behavior if the label value is still empty,
69
73
  //upon rendering the component
70
74
  it('has no background hover effect when label is empty', async () => {
71
- const el: Checkbox = await fixture(html`
72
- <temba-checkbox name="My Checkbox"></temba-checkbox>
73
- `);
75
+ const el: Checkbox = await getCheckbox({
76
+ name: 'My Checkbox'
77
+ });
78
+
74
79
  expect(el.label).to.equal(undefined);
75
80
  //the ".wrapper.label" style results in the background hover effect
76
81
  const wrapperDivEl = el.shadowRoot.querySelector(
@@ -84,9 +89,11 @@ describe('temba-checkbox', () => {
84
89
  });
85
90
 
86
91
  it('has no background hover effect when label is whitespace', async () => {
87
- const el: Checkbox = await fixture(html`
88
- <temba-checkbox name="My Checkbox" label=" "></temba-checkbox>
89
- `);
92
+ const el: Checkbox = await getCheckbox({
93
+ name: 'My Checkbox',
94
+ label: ''
95
+ });
96
+
90
97
  expect(el.label).to.equal('');
91
98
  //the ".wrapper.label" style results in the background hover effect
92
99
  const wrapperDivEl = el.shadowRoot.querySelector(
@@ -167,13 +174,10 @@ describe('temba-checkbox', () => {
167
174
  });
168
175
 
169
176
  it('aligns help text with label when both are present', async () => {
170
- const el: Checkbox = await fixture(html`
171
- <temba-checkbox
172
- label="Checkbox with help"
173
- help_text="This help text should align with the label text"
174
- >
175
- </temba-checkbox>
176
- `);
177
+ const el: Checkbox = (await getCheckbox({
178
+ label: 'Checkbox with help',
179
+ help_text: 'This help text should align with the label text'
180
+ })) as unknown as Checkbox;
177
181
 
178
182
  expect(el.label).to.equal('Checkbox with help');
179
183
  expect(el.helpText).to.equal(
@@ -73,7 +73,8 @@ describe('temba-contact-chat', () => {
73
73
  mockedNow.restore();
74
74
  });
75
75
 
76
- it('show history and show chatbox if contact is active', async () => {
76
+ // temporarily disabled as it's too flaky in CI
77
+ xit('show history and show chatbox if contact is active', async () => {
77
78
  // we are a StoreElement, so load a store first
78
79
  await loadStore();
79
80
  const chat: ContactChat = await getContactChat({
@@ -289,12 +289,6 @@ describe(TAG, () => {
289
289
 
290
290
  // Verify position was adjusted for right edge
291
291
  expect(dropdown.dropdownStyle).to.have.property('left');
292
-
293
- // Screenshot positioned dropdown
294
- await assertScreenshot(
295
- 'dropdown/right-edge-collision',
296
- getDropdownClip(dropdown)
297
- );
298
292
  } finally {
299
293
  // Restore original method
300
294
  dropdownDiv.getBoundingClientRect = originalGetBoundingClientRect;
@@ -343,12 +337,6 @@ describe(TAG, () => {
343
337
  expect(dropdown.dropdownStyle).to.have.property('top');
344
338
  expect(dropdown.arrowStyle).to.have.property('transform');
345
339
  expect(dropdown.arrowStyle['transform']).to.include('rotate(180deg)');
346
-
347
- // Screenshot positioned dropdown
348
- await assertScreenshot(
349
- 'dropdown/bottom-edge-collision',
350
- getDropdownClip(dropdown)
351
- );
352
340
  } finally {
353
341
  // Restore original method
354
342
  dropdownDiv.getBoundingClientRect = originalGetBoundingClientRect;
@@ -223,12 +223,19 @@ describe('EditorNode', () => {
223
223
  color: '#ff0000'
224
224
  };
225
225
 
226
- const result = (editorNode as any).renderTitle(config);
226
+ const mockAction: Action = {
227
+ type: 'send_msg',
228
+ uuid: 'test-action'
229
+ };
230
+
231
+ const result = (editorNode as any).renderTitle(config, mockAction, 0);
227
232
  const container = await fixture(html`<div>${result}</div>`);
228
233
 
229
- const title = container.querySelector('.title');
234
+ const title = container.querySelector('.cn-title');
230
235
  expect(title).to.exist;
231
- expect(title?.textContent?.trim()).to.equal('Test Action');
236
+
237
+ const nameElement = title?.querySelector('.name');
238
+ expect(nameElement?.textContent?.trim()).to.equal('Test Action');
232
239
  expect(title?.getAttribute('style')).to.contain('background:#ff0000');
233
240
  });
234
241
  });
@@ -1046,7 +1053,7 @@ describe('EditorNode', () => {
1046
1053
  expect(actionElement?.classList.contains('removing')).to.be.true;
1047
1054
 
1048
1055
  // Check that title shows "Remove?"
1049
- const titleElement = container.querySelector('.title .name');
1056
+ const titleElement = container.querySelector('.cn-title .name');
1050
1057
  expect(titleElement?.textContent?.trim()).to.equal('Remove?');
1051
1058
  });
1052
1059
 
@@ -1,23 +1,22 @@
1
- import { html, fixture, expect } from '@open-wc/testing';
2
- import { Checkbox } from '../src/form/Checkbox';
3
- import { assertScreenshot, getClip } from './utils.test';
1
+ import { expect } from '@open-wc/testing';
2
+ import { assertScreenshot, getClip, getComponent } from './utils.test';
3
+ import { TextInput } from '../src/form/TextInput';
4
4
 
5
5
  describe('FormElement markdown integration', () => {
6
- it('renders checkbox with markdown errors', async () => {
7
- const checkbox: Checkbox = await fixture(html`
8
- <temba-checkbox
9
- label="Accept Terms"
10
- .errors=${[
11
- 'Please read the **terms and conditions** at [this link](https://example.com)',
12
- 'This field *requires* acceptance'
13
- ]}
14
- ></temba-checkbox>
15
- `);
6
+ it('renders textinput with markdown errors', async () => {
7
+ const el: TextInput = (await getComponent('temba-textinput', {
8
+ name: 'my_textinput'
9
+ })) as TextInput;
16
10
 
17
- await checkbox.updateComplete;
11
+ el.errors = [
12
+ 'You must agree to the **terms and conditions** at [this link](https://example.com)',
13
+ 'This field *requires* a value'
14
+ ];
15
+
16
+ await el.updateComplete;
18
17
 
19
18
  // Check that errors are rendered with markdown directly in checkbox shadow root
20
- const errorElements = checkbox.shadowRoot.querySelectorAll('.alert-error');
19
+ const errorElements = el.shadowRoot.querySelectorAll('.alert-error');
21
20
  expect(errorElements.length).to.equal(2);
22
21
 
23
22
  // First error should have bold text and link
@@ -36,8 +35,8 @@ describe('FormElement markdown integration', () => {
36
35
  expect(italicElement.textContent).to.equal('requires');
37
36
 
38
37
  await assertScreenshot(
39
- 'integration/checkbox-markdown-errors',
40
- getClip(checkbox)
38
+ 'integration/textinput-markdown-errors',
39
+ getClip(el)
41
40
  );
42
41
  });
43
42
  });
@@ -18,7 +18,11 @@ const assertDialogScreenshot = async (
18
18
  const dialog = el.shadowRoot
19
19
  .querySelector('temba-dialog')
20
20
  .shadowRoot.querySelector('.dialog-container') as HTMLElement;
21
- await assertScreenshot(screenshotName, getClip(dialog));
21
+ const clip = getClip(dialog);
22
+ // Adjust width to show full dialog with proper padding
23
+ const dialogRect = dialog.getBoundingClientRect();
24
+ clip.width = dialogRect.width + 20; // 10px padding on each side
25
+ await assertScreenshot(screenshotName, clip);
22
26
  };
23
27
 
24
28
  describe('temba-node-editor', () => {
@@ -337,48 +341,6 @@ describe('temba-node-editor', () => {
337
341
  expect(shadowRoot).to.not.be.null;
338
342
  });
339
343
 
340
- it('renders different action types correctly', async () => {
341
- const actionTypes = [
342
- {
343
- type: 'send_msg',
344
- data: { text: 'Message', quick_replies: [] }
345
- },
346
- {
347
- type: 'set_run_result',
348
- data: { name: 'result', value: 'value' }
349
- },
350
- {
351
- type: 'set_contact_name',
352
- data: { name: 'John Doe' }
353
- },
354
- {
355
- type: 'set_contact_language',
356
- data: { language: 'eng' }
357
- }
358
- ];
359
-
360
- for (const actionType of actionTypes) {
361
- const action = {
362
- uuid: `test-${actionType.type}`,
363
- type: actionType.type,
364
- ...actionType.data
365
- };
366
-
367
- const el = (await fixture(html`
368
- <temba-node-editor
369
- .action=${action}
370
- .isOpen=${true}
371
- ></temba-node-editor>
372
- `)) as NodeEditorElement;
373
-
374
- await el.updateComplete;
375
- expect(el.shadowRoot).to.not.be.null;
376
- expect(el.action.type).to.equal(actionType.type);
377
-
378
- await assertDialogScreenshot(el, `editor/${actionType.type}`);
379
- }
380
- });
381
-
382
344
  it('displays bubble count for group value counts', async () => {
383
345
  const action = {
384
346
  uuid: 'test-action-uuid',
@@ -572,7 +572,7 @@ describe('temba-run-list', () => {
572
572
  contact: {
573
573
  name: 'John Doe',
574
574
  urn: 'tel:+1234567890',
575
- anon_display: '1234567890'
575
+ ref: 'E2E6MX'
576
576
  },
577
577
  modified_on: '2023-12-01T10:30:00.000Z',
578
578
  exited_on: '2023-12-01T10:30:00.000Z',
@@ -591,7 +591,7 @@ describe('temba-run-list', () => {
591
591
  contact: {
592
592
  name: null,
593
593
  urn: 'tel:+1234567890',
594
- anon_display: '1234567890'
594
+ ref: 'E2E6MX'
595
595
  },
596
596
  modified_on: '2023-12-01T10:30:00.000Z',
597
597
  exited_on: null,
@@ -110,7 +110,7 @@ const getResponse = (endpoint: string, options = { method: 'GET' }) => {
110
110
  before(async () => {
111
111
  normalFetch = window.fetch;
112
112
  stub(window, 'fetch').callsFake(getResponse);
113
- await setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });
113
+ await setViewport({ width: 1920, height: 1080, deviceScaleFactor: 2 });
114
114
  });
115
115
 
116
116
  after(() => {
@@ -253,6 +253,7 @@ export const getClip = (ele: HTMLElement) => {
253
253
  clip = ele.shadowRoot.firstElementChild.getBoundingClientRect();
254
254
  }
255
255
 
256
+ // add some padding
256
257
  const padding = 10;
257
258
  const width = clip.width + padding * 2;
258
259
  const height = clip.height + padding * 2;
@@ -9,7 +9,7 @@
9
9
  "uuid": "contact-uuid-1",
10
10
  "name": "John Doe",
11
11
  "urn": "tel:+1234567890",
12
- "anon_display": "1234567890"
12
+ "ref": "E2E6MX"
13
13
  },
14
14
  "flow": {
15
15
  "uuid": "flow-uuid-1",
@@ -23,7 +23,7 @@
23
23
  "values": {
24
24
  "name": {
25
25
  "name": "Name",
26
- "key": "name",
26
+ "key": "name",
27
27
  "value": "John Doe",
28
28
  "category": "Text"
29
29
  },
@@ -37,12 +37,12 @@
37
37
  },
38
38
  {
39
39
  "id": 2,
40
- "uuid": "run-uuid-2",
40
+ "uuid": "run-uuid-2",
41
41
  "contact": {
42
42
  "uuid": "contact-uuid-2",
43
43
  "name": "Jane Smith",
44
44
  "urn": "tel:+1987654321",
45
- "anon_display": "1987654321"
45
+ "ref": "3MWB69"
46
46
  },
47
47
  "flow": {
48
48
  "uuid": "flow-uuid-1",
@@ -59,10 +59,10 @@
59
59
  "id": 3,
60
60
  "uuid": "run-uuid-3",
61
61
  "contact": {
62
- "uuid": "contact-uuid-3",
62
+ "uuid": "contact-uuid-3",
63
63
  "name": null,
64
64
  "urn": "tel:+1122334455",
65
- "anon_display": "1122334455"
65
+ "ref": "Q3GP9G"
66
66
  },
67
67
  "flow": {
68
68
  "uuid": "flow-uuid-1",
@@ -89,7 +89,7 @@
89
89
  "uuid": "contact-uuid-4",
90
90
  "name": "Active User",
91
91
  "urn": "tel:+1555666777",
92
- "anon_display": "1555666777"
92
+ "ref": "U6Y6T5"
93
93
  },
94
94
  "flow": {
95
95
  "uuid": "flow-uuid-1",
@@ -109,7 +109,7 @@
109
109
  "uuid": "contact-uuid-5",
110
110
  "name": "Pending User",
111
111
  "urn": "tel:+1888999000",
112
- "anon_display": "1888999000"
112
+ "ref": "SJPWLU"
113
113
  },
114
114
  "flow": {
115
115
  "uuid": "flow-uuid-1",
package/web-dev-mock.mjs CHANGED
@@ -440,8 +440,63 @@ export function handleMinioUpload(context) {
440
440
  });
441
441
  }
442
442
 
443
- // Handle label creation for the labels API
444
- export function handleLabelCreation(context) {
443
+ // Entity creation configurations
444
+ const ENTITY_CONFIGS = {
445
+ labels: {
446
+ filePath: './static/api/labels.json',
447
+ createEntity: (name) => ({
448
+ uuid: uuidv4(),
449
+ name: name.trim(),
450
+ count: 0
451
+ }),
452
+ nameField: 'name',
453
+ entityType: 'Label'
454
+ },
455
+ fields: {
456
+ filePath: './static/api/fields.json',
457
+ createEntity: (name) => ({
458
+ key: name.trim().toLowerCase().replace(/\s+/g, '_'),
459
+ name: name.trim(),
460
+ type: 'text',
461
+ featured: false,
462
+ priority: 0,
463
+ usages: {
464
+ flows: 0,
465
+ groups: 0,
466
+ campaign_events: 0
467
+ },
468
+ agent_access: 'view',
469
+ label: name.trim(),
470
+ value_type: 'text'
471
+ }),
472
+ nameField: 'name',
473
+ uniqueField: 'key', // check uniqueness by key for fields
474
+ entityType: 'Field'
475
+ },
476
+ groups: {
477
+ filePath: './static/api/groups.json',
478
+ createEntity: (name) => ({
479
+ uuid: uuidv4(),
480
+ name: name.trim(),
481
+ query: null,
482
+ status: 'ready',
483
+ count: 0
484
+ }),
485
+ nameField: 'name',
486
+ entityType: 'Group'
487
+ }
488
+ };
489
+
490
+ // Generic entity creation handler for labels, fields, and groups
491
+ export function handleEntityCreation(entityType, context) {
492
+ const config = ENTITY_CONFIGS[entityType];
493
+
494
+ if (!config) {
495
+ context.status = 400;
496
+ context.body = JSON.stringify({ error: `Unsupported entity type: ${entityType}` });
497
+ return Promise.resolve();
498
+ }
499
+
445
500
  return new Promise((resolve) => {
446
501
  let body = '';
447
502
 
@@ -452,67 +507,67 @@ export function handleLabelCreation(context) {
452
507
  context.req.on('end', () => {
453
508
  try {
454
509
  const requestData = JSON.parse(body);
455
- const labelName = requestData.name || '';
510
+ const entityName = requestData[config.nameField] || '';
456
511
 
457
- if (!labelName.trim()) {
512
+ if (!entityName.trim()) {
458
513
  context.status = 400;
459
- context.body = JSON.stringify({ error: 'Label name is required' });
514
+ context.body = JSON.stringify({ error: `${config.entityType} name is required` });
460
515
  resolve();
461
516
  return;
462
517
  }
463
518
 
464
- // Read existing labels file
465
- const labelsPath = './static/api/labels.json';
466
- let labelsData;
519
+ // Read existing data file
520
+ let entityData;
467
521
 
468
522
  try {
469
- labelsData = JSON.parse(fs.readFileSync(labelsPath, 'utf-8'));
523
+ entityData = JSON.parse(fs.readFileSync(config.filePath, 'utf-8'));
470
524
  } catch (error) {
471
525
  // If file doesn't exist, create basic structure
472
- labelsData = {
526
+ entityData = {
473
527
  next: null,
474
528
  previous: null,
475
529
  results: []
476
530
  };
477
531
  }
478
532
 
479
- // Check if label already exists
480
- const existingLabel = labelsData.results.find(
481
- label => label.name.toLowerCase() === labelName.trim().toLowerCase()
533
+ // Check if entity already exists
534
+ const uniqueField = config.uniqueField || config.nameField;
535
+ const checkValue = uniqueField === 'key'
536
+ ? entityName.trim().toLowerCase().replace(/\s+/g, '_')
537
+ : entityName.trim().toLowerCase();
538
+
539
+ const existingEntity = entityData.results.find(
540
+ entity => entity[uniqueField].toLowerCase() === checkValue
482
541
  );
483
542
 
484
- if (existingLabel) {
485
- // Return existing label
543
+ if (existingEntity) {
544
+ // Return existing entity
486
545
  context.contentType = 'application/json';
487
- context.body = JSON.stringify(existingLabel);
546
+ context.body = JSON.stringify(existingEntity);
488
547
  resolve();
489
548
  return;
490
549
  }
491
550
 
492
- // Create new label with UUID
493
- const newLabel = {
494
- uuid: uuidv4(),
495
- name: labelName.trim(),
496
- count: 0
497
- };
551
+ // Create new entity
552
+ const newEntity = config.createEntity(entityName);
498
553
 
499
- // Add to labels data
500
- labelsData.results.push(newLabel);
554
+ // Add to entity data
555
+ entityData.results.push(newEntity);
501
556
 
502
557
  // Write back to file
503
- fs.writeFileSync(labelsPath, JSON.stringify(labelsData, null, 2));
558
+ fs.writeFileSync(config.filePath, JSON.stringify(entityData, null, 2));
504
559
 
505
- // Return the new label
560
+ // Return the new entity
506
561
  context.contentType = 'application/json';
507
- context.body = JSON.stringify(newLabel);
562
+ context.body = JSON.stringify(newEntity);
508
563
 
509
- console.log('📝 Label created:', newLabel);
564
+ console.log(`📝 ${config.entityType} created:`, newEntity);
510
565
 
511
566
  } catch (error) {
512
- console.error('Label creation error:', error);
567
+ console.error(`${config.entityType} creation error:`, error);
513
568
  context.status = 500;
514
569
  context.body = JSON.stringify({
515
- error: 'Label creation failed',
570
+ error: `${config.entityType} creation failed`,
516
571
  details: error.message
517
572
  });
518
573
  }
@@ -521,3 +576,4 @@ export function handleLabelCreation(context) {
521
576
  });
522
577
  });
523
578
  }
579
+