@nyaruka/temba-components 0.123.0 → 0.124.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 (146) hide show
  1. package/.github/copilot-instructions.md +22 -4
  2. package/CHANGELOG.md +21 -0
  3. package/TEST_OPTIMIZATION.md +158 -0
  4. package/demo/alert/example.html +65 -0
  5. package/demo/button/example.html +71 -0
  6. package/demo/chart/example.html +56 -0
  7. package/demo/checkbox/example.html +72 -0
  8. package/demo/compose/example.html +72 -0
  9. package/demo/data/images/gus.png +0 -0
  10. package/demo/data/images/purrington.jpg +0 -0
  11. package/demo/data/server/opened-tickets.json +40 -0
  12. package/demo/data/server/response-time.json +27 -0
  13. package/demo/datepicker/example.html +69 -0
  14. package/demo/dialog/example.html +107 -0
  15. package/demo/dropdown/example.html +99 -0
  16. package/demo/index.html +152 -430
  17. package/demo/misc/example.html +72 -0
  18. package/demo/progress/example.html +59 -0
  19. package/demo/select/drag-and-drop.html +142 -0
  20. package/demo/select/example.html +82 -0
  21. package/demo/select/multi.html +73 -0
  22. package/demo/slider/example.html +59 -0
  23. package/demo/sortable-list/example.html +99 -0
  24. package/demo/styles.css +183 -0
  25. package/demo/tabs/example.html +91 -0
  26. package/demo/textinput/completion.html +56 -0
  27. package/demo/textinput/example.html +61 -0
  28. package/dist/temba-components.js +323 -191
  29. package/dist/temba-components.js.map +1 -1
  30. package/out-tsc/src/chart/TembaChart.js +19 -16
  31. package/out-tsc/src/chart/TembaChart.js.map +1 -1
  32. package/out-tsc/src/fields/FieldManager.js +27 -34
  33. package/out-tsc/src/fields/FieldManager.js.map +1 -1
  34. package/out-tsc/src/flow/Editor.js +1 -1
  35. package/out-tsc/src/flow/Editor.js.map +1 -1
  36. package/out-tsc/src/list/SortableList.js +257 -60
  37. package/out-tsc/src/list/SortableList.js.map +1 -1
  38. package/out-tsc/src/omnibox/Omnibox.js +1 -1
  39. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  40. package/out-tsc/src/select/Select.js +198 -38
  41. package/out-tsc/src/select/Select.js.map +1 -1
  42. package/out-tsc/src/thumbnail/Thumbnail.js +1 -1
  43. package/out-tsc/src/thumbnail/Thumbnail.js.map +1 -1
  44. package/out-tsc/src/webchat/WebChat.js +5 -2
  45. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  46. package/out-tsc/test/temba-chart.test.js +1 -1
  47. package/out-tsc/test/temba-chart.test.js.map +1 -1
  48. package/out-tsc/test/temba-compose.test.js +6 -30
  49. package/out-tsc/test/temba-compose.test.js.map +1 -1
  50. package/out-tsc/test/temba-contact-chat.test.js +1 -2
  51. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  52. package/out-tsc/test/temba-dropdown.test.js +1 -1
  53. package/out-tsc/test/temba-dropdown.test.js.map +1 -1
  54. package/out-tsc/test/temba-flow-editor-node.test.js +273 -0
  55. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -0
  56. package/out-tsc/test/temba-flow-editor.test.js +244 -0
  57. package/out-tsc/test/temba-flow-editor.test.js.map +1 -0
  58. package/out-tsc/test/temba-flow-plumber.test.js +145 -0
  59. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -0
  60. package/out-tsc/test/temba-flow-render.test.js +171 -0
  61. package/out-tsc/test/temba-flow-render.test.js.map +1 -0
  62. package/out-tsc/test/temba-omnibox.test.js +6 -3
  63. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  64. package/out-tsc/test/temba-select.test.js +183 -53
  65. package/out-tsc/test/temba-select.test.js.map +1 -1
  66. package/out-tsc/test/temba-sortable-list.test.js +91 -15
  67. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  68. package/out-tsc/test/temba-toast.test.js +1 -2
  69. package/out-tsc/test/temba-toast.test.js.map +1 -1
  70. package/out-tsc/test/temba-utils-index.test.js +2 -2
  71. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  72. package/out-tsc/test/temba-webchat-lightbox-fix.test.js +42 -0
  73. package/out-tsc/test/temba-webchat-lightbox-fix.test.js.map +1 -0
  74. package/out-tsc/test/utils.test.js +58 -0
  75. package/out-tsc/test/utils.test.js.map +1 -1
  76. package/package.json +2 -3
  77. package/screenshots/truth/flow/editor-basic.png +0 -0
  78. package/screenshots/truth/list/fields-dragging.png +0 -0
  79. package/screenshots/truth/list/sortable-dragging.png +0 -0
  80. package/screenshots/truth/list/sortable-dropped.png +0 -0
  81. package/screenshots/truth/list/sortable.png +0 -0
  82. package/screenshots/truth/omnibox/selected.png +0 -0
  83. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  84. package/screenshots/truth/select/disabled-selection.png +0 -0
  85. package/screenshots/truth/select/disabled.png +0 -0
  86. package/screenshots/truth/select/embedded.png +0 -0
  87. package/screenshots/truth/select/empty-options.png +0 -0
  88. package/screenshots/truth/select/expression-selected.png +0 -0
  89. package/screenshots/truth/select/expressions.png +0 -0
  90. package/screenshots/truth/select/functions.png +0 -0
  91. package/screenshots/truth/select/local-options.png +0 -0
  92. package/screenshots/truth/select/multi-reorder-final.png +0 -0
  93. package/screenshots/truth/select/multi-reorder-initial.png +0 -0
  94. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  95. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  96. package/screenshots/truth/select/remote-options.png +0 -0
  97. package/screenshots/truth/select/search-enabled.png +0 -0
  98. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  99. package/screenshots/truth/select/search-selected-focus.png +0 -0
  100. package/screenshots/truth/select/search-selected.png +0 -0
  101. package/screenshots/truth/select/search-with-selected.png +0 -0
  102. package/screenshots/truth/select/searching.png +0 -0
  103. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  104. package/screenshots/truth/select/selected-multi.png +0 -0
  105. package/screenshots/truth/select/selected-single.png +0 -0
  106. package/screenshots/truth/select/selection-clearable.png +0 -0
  107. package/screenshots/truth/select/static-initial-value.png +0 -0
  108. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  109. package/screenshots/truth/select/truncated-selection.png +0 -0
  110. package/screenshots/truth/select/with-placeholder.png +0 -0
  111. package/screenshots/truth/select/without-placeholder.png +0 -0
  112. package/screenshots/truth/templates/default.png +0 -0
  113. package/screenshots/truth/templates/unapproved.png +0 -0
  114. package/screenshots/truth/webchat/connected-state.png +0 -0
  115. package/src/chart/TembaChart.ts +20 -16
  116. package/src/fields/FieldManager.ts +30 -38
  117. package/src/flow/Editor.ts +1 -1
  118. package/src/list/SortableList.ts +291 -67
  119. package/src/omnibox/Omnibox.ts +1 -1
  120. package/src/select/Select.ts +213 -42
  121. package/src/thumbnail/Thumbnail.ts +1 -1
  122. package/src/webchat/WebChat.ts +5 -2
  123. package/test/temba-chart.test.ts +1 -1
  124. package/test/temba-compose.test.ts +11 -38
  125. package/test/temba-contact-chat.test.ts +4 -6
  126. package/test/temba-dropdown.test.ts +1 -1
  127. package/test/temba-flow-editor-node.test.ts +344 -0
  128. package/test/temba-flow-editor.test.ts +301 -0
  129. package/test/temba-flow-plumber.test.ts +189 -0
  130. package/test/temba-flow-render.test.ts +220 -0
  131. package/test/temba-omnibox.test.ts +7 -3
  132. package/test/temba-select.test.ts +247 -79
  133. package/test/temba-sortable-list.test.ts +108 -15
  134. package/test/temba-toast.test.ts +2 -2
  135. package/test/temba-utils-index.test.ts +2 -2
  136. package/test/temba-webchat-lightbox-fix.test.ts +57 -0
  137. package/test/utils.test.ts +88 -0
  138. package/web-test-runner.config.mjs +4 -2
  139. package/.storybook/main.js +0 -14
  140. package/.storybook/preview-head.html +0 -1
  141. package/.storybook/preview.js +0 -17
  142. package/demo/agents.html +0 -147
  143. package/demo/old.html +0 -573
  144. package/demo/remote.html +0 -3
  145. package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
  146. package/stories/temba-checkbox.stories.md +0 -37
@@ -0,0 +1,57 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { WebChat } from '../src/webchat/WebChat';
3
+ import { getComponent } from './utils.test';
4
+
5
+ const getWebChat = async (attrs: any = {}) => {
6
+ const webChat = (await getComponent(
7
+ 'temba-webchat',
8
+ attrs,
9
+ '',
10
+ 400,
11
+ 600
12
+ )) as WebChat;
13
+ return webChat;
14
+ };
15
+
16
+ describe('WebChat Lightbox Fix', () => {
17
+ afterEach(() => {
18
+ // Clean up any lightbox elements after each test
19
+ const lightboxes = document.querySelectorAll('temba-lightbox');
20
+ lightboxes.forEach((lightbox) => lightbox.remove());
21
+ });
22
+
23
+ it('should only create one lightbox element even with multiple WebChat instances', async () => {
24
+ // Create first WebChat instance
25
+ await getWebChat({
26
+ channel: 'test-channel-1'
27
+ });
28
+
29
+ // Verify lightbox was created
30
+ let lightboxes = document.querySelectorAll('temba-lightbox');
31
+ expect(lightboxes.length).to.equal(1);
32
+
33
+ // Create second WebChat instance
34
+ await getWebChat({
35
+ channel: 'test-channel-2'
36
+ });
37
+
38
+ // Verify still only one lightbox exists
39
+ lightboxes = document.querySelectorAll('temba-lightbox');
40
+ expect(lightboxes.length).to.equal(1);
41
+ });
42
+
43
+ it('should not create lightbox if one already exists', async () => {
44
+ // Manually create a lightbox first
45
+ const existingLightbox = document.createElement('temba-lightbox');
46
+ document.body.appendChild(existingLightbox);
47
+
48
+ // Create WebChat instance
49
+ await getWebChat({
50
+ channel: 'test-channel'
51
+ });
52
+
53
+ // Verify still only one lightbox exists
54
+ const lightboxes = document.querySelectorAll('temba-lightbox');
55
+ expect(lightboxes.length).to.equal(1);
56
+ });
57
+ });
@@ -11,6 +11,10 @@ import { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';
11
11
  import MouseHelper from './MouseHelper';
12
12
  import { Store } from '../src/store/Store';
13
13
  import { replace, stub } from 'sinon';
14
+ import { Select, SelectOption } from '../src/select/Select';
15
+ import { Options } from '../src/options/Options';
16
+ import { Attachment } from '../src/interfaces';
17
+ import { Compose } from '../src/compose/Compose';
14
18
 
15
19
  export interface CodeMock {
16
20
  endpoint: RegExp;
@@ -279,3 +283,87 @@ export const mockNow = (isodate: string) => {
279
283
  return now;
280
284
  });
281
285
  };
286
+
287
+ export const getOptions = (select: Select<SelectOption>): Options => {
288
+ return select.shadowRoot.querySelector('temba-options[visible]');
289
+ };
290
+
291
+ export const clickOption = async (
292
+ clock: any,
293
+ select: Select<SelectOption>,
294
+ index: number
295
+ ) => {
296
+ const options = getOptions(select);
297
+ const option = options.shadowRoot.querySelector(
298
+ `[data-option-index="${index}"]`
299
+ ) as HTMLDivElement;
300
+
301
+ await mouseClickElement(option);
302
+ await options.updateComplete;
303
+ await select.updateComplete;
304
+ await clock.runAll();
305
+
306
+ checkTimers(clock);
307
+ };
308
+ export const openSelect = async (clock: any, select: Select<SelectOption>) => {
309
+ const container = select.shadowRoot.querySelector('.select-container');
310
+ container.dispatchEvent(new MouseEvent('click', { bubbles: true }));
311
+
312
+ clock.runAll();
313
+
314
+ // add more explicit waiting and clock ticks
315
+ await select.updateComplete;
316
+ clock.runAll();
317
+
318
+ // reduce wait time for options to become visible
319
+ await waitFor(25);
320
+ clock.runAll();
321
+ };
322
+
323
+ export const openAndClick = async (
324
+ clock: any,
325
+ select: Select<SelectOption>,
326
+ idx: number
327
+ ) => {
328
+ await openSelect(clock, select);
329
+
330
+ // Add this line to ensure proper timing when running as part of a test suite
331
+ await select.updateComplete;
332
+ clock.tick(25); // Reduced from 50 to give minimum time for options to render
333
+
334
+ await clickOption(clock, select, idx);
335
+ };
336
+
337
+ // valid = attachments that are uploaded sent to the server when the user clicks send
338
+ export const getValidAttachments = (numFiles = 2): Attachment[] => {
339
+ const attachments = [];
340
+ let index = 1;
341
+ while (index <= numFiles) {
342
+ const s = 's' + index;
343
+ const attachment = {
344
+ uuid: s,
345
+ content_type: 'image/png',
346
+ type: 'image/png',
347
+ filename: 'name_' + s,
348
+ url: 'url_' + s,
349
+ size: 1024,
350
+ error: null
351
+ } as Attachment;
352
+ attachments.push(attachment);
353
+ index++;
354
+ }
355
+ return attachments;
356
+ };
357
+
358
+ export const updateComponent = async (
359
+ compose: Compose,
360
+ text?: string,
361
+ attachments?: Attachment[]
362
+ ): Promise<void> => {
363
+ compose.initialText = text ? text : '';
364
+ compose.currentAttachments = attachments ? attachments : [];
365
+ await compose.updateComplete;
366
+ };
367
+ export const getValidText = () => {
368
+ return 'sà-wàd-dee!';
369
+ };
@@ -150,8 +150,9 @@ const wireScreenshots = async (page, context, wait, replaceScreenshots) => {
150
150
  const testFile = await getPath(TEST, filename);
151
151
  const truthFile = await getPath(TRUTH, filename);
152
152
 
153
+ // Only wait for network idle if explicitly requested
153
154
  if (wait) {
154
- await page.waitForNetworkIdle();
155
+ await page.waitForNetworkIdle({ idleTime: 100, timeout: 1000 });
155
156
  }
156
157
 
157
158
  if (!(await fileExists(truthFile))) {
@@ -302,9 +303,10 @@ export default {
302
303
  files: '**/test/**/*.test.ts',
303
304
  nodeResolve: true,
304
305
  setupFiles: ['./test-setup.js'],
306
+ concurrency: 4,
305
307
  testFramework: {
306
308
  config: {
307
- timeout: '10000',
309
+ timeout: '5000',
308
310
  },
309
311
  },
310
312
 
@@ -1,14 +0,0 @@
1
- module.exports = {
2
- stories: ['../stories/**/*.stories.{js,md,mdx}'],
3
- addons: [
4
- 'storybook-prebuilt/addon-knobs/register.js',
5
- 'storybook-prebuilt/addon-docs/register.js',
6
- 'storybook-prebuilt/addon-viewport/register.js'
7
- ],
8
- esDevServer: {
9
- // custom es-dev-server options
10
- nodeResolve: true,
11
- watch: true,
12
- open: true
13
- }
14
- };
@@ -1 +0,0 @@
1
- <link type="text/css" rel="stylesheet" href="https://textit.com/sitestatic/css/temba-components.css"/>
@@ -1,17 +0,0 @@
1
- import { addParameters, setCustomElements } from '@open-wc/demoing-storybook';
2
-
3
- addParameters({
4
- docs: {
5
- iframeHeight: '200px'
6
- }
7
- });
8
-
9
- async function run() {
10
- const customElements = await (
11
- await fetch(new URL('../custom-elements.json', import.meta.url))
12
- ).json();
13
-
14
- setCustomElements(customElements);
15
- }
16
-
17
- run();
package/demo/agents.html DELETED
@@ -1,147 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <link
6
- href="/sitestatic/css/temba-components.css"
7
- rel="stylesheet"
8
- type="text/css"
9
- />
10
-
11
- <link
12
- href="/sitestatic/css/tailwind.css"
13
- rel="stylesheet"
14
- type="text/css"
15
- />
16
-
17
- <link
18
- href="https://fonts.googleapis.com/css?family=Roboto+Mono:300|Roboto:300,400,500"
19
- rel="stylesheet"
20
- />
21
-
22
- <style>
23
- html {
24
- --color-text-dark: #555 !important;
25
- }
26
-
27
- temba-select[name='ticket-filter'] {
28
- margin-bottom: 0.5em;
29
- margin-top: -0.4em;
30
- }
31
-
32
- .ticket-list {
33
- flex-basis: 300px;
34
- flex-grow: 0;
35
- flex-shrink: 0;
36
- transition: all 200ms ease-in;
37
- }
38
-
39
- @media only screen and (max-width: 1024px) {
40
- .ticket-list {
41
- flex-basis: 200px;
42
- }
43
- }
44
-
45
- @media only screen and (max-width: 768px) {
46
- .ticket-list {
47
- flex-basis: 125px;
48
- }
49
- }
50
-
51
- </style>
52
- <script type="module">
53
- import '../out-tsc/temba-modules.js';
54
- </script>
55
-
56
- <script language="javascript">
57
-
58
- function handleContactChange(event) {
59
- const ticket = event.target.value;
60
- const chat = document.querySelector("temba-contact-chat");
61
- chat.ticket = ticket;
62
- }
63
-
64
- function handleContactHistoryUpdate(event) {
65
- // tell our list to refresh since we know things happened
66
- const tickets = document.querySelector("temba-tickets");
67
- tickets.refresh();
68
- }
69
-
70
- function handleTicketsRefreshed(event) {
71
- // tell our chat to refresh since we know things are new in our list
72
- const chat = document.querySelector("temba-contact-chat");
73
- chat.refresh();
74
- }
75
-
76
- function handleFilterChanged(event) {
77
- const tickets = document.querySelector("temba-tickets");
78
- const chat = document.querySelector("temba-contact-chat");
79
-
80
- // if we have auto selecting, don't clear the chat
81
- if (!tickets.nextSelection) {
82
- chat.ticket = null;
83
- }
84
-
85
- const filter = event.target.values[0].value;
86
- if (filter === "O") {
87
- tickets.endpoint = "/contact/tickets/?_format=json&folder=open"
88
- } else {
89
- tickets.endpoint = "/contact/tickets/?_format=json&folder=closed"
90
- }
91
- }
92
-
93
- function handleTicketChanged(event) {
94
- const ticket = event.detail.ticket;
95
- const focus = event.detail.focus;
96
-
97
- const filter = document.querySelector('temba-select[name="ticket-filter"]');
98
- const tickets = document.querySelector("temba-tickets");
99
-
100
- if (focus) {
101
- tickets.setNextSelection(ticket.uuid);
102
- filter.setSelection(ticket.status);
103
- } else {
104
- // no focus, just refresh
105
- tickets.refresh();
106
- }
107
- }
108
-
109
- </script>
110
-
111
-
112
- </head>
113
- <body>
114
-
115
- <temba-store
116
- completions="/mr/docs/en-us/completion.json"
117
- functions="/mr/docs/en-us/functions.json"
118
- fields="/api/v2/fields.json"
119
- globals="/api/v2/globals.json"
120
- groups="/api/v2/groups.json"
121
- ></temba-store>
122
-
123
- <div class="flex-col">
124
- <div class="flex" style="height:100vh">
125
-
126
- <div class="ticket-list m-4 mr-2 flex flex-col">
127
-
128
- <temba-select name="ticket-filter" onchange="handleFilterChanged(event)">
129
- <temba-option name="Open" value="O" icon="inbox"></temba-option>
130
- <temba-option name="Closed" value="C" icon="check"></temba-option>
131
- </temba-select>
132
-
133
-
134
- <div class="flex flex-grow">
135
- <temba-tickets endpoint="/contact/tickets/?_format=json&folder=open" @temba-refreshed="handleTicketsRefreshed(event)" onchange="handleContactChange(event)"></temba-tickets>
136
- </div>
137
-
138
- </div>
139
-
140
- <div class="flex-grow flex-col h-full py-4 pb-4 pr-4">
141
- <temba-contact-chat @temba-refreshed="handleContactHistoryUpdate(event)" @temba-content-changed="handleTicketChanged(event)"></temba-contact-chat>
142
- </div>
143
-
144
- </div>
145
- </div>
146
- </body>
147
- </html>