@nyaruka/temba-components 0.112.0 → 0.114.0

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 (130) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/demo/index.html +1 -1
  3. package/dist/temba-components.js +794 -968
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/aliaseditor/AliasEditor.js.map +1 -1
  6. package/out-tsc/src/button/Button.js +6 -2
  7. package/out-tsc/src/button/Button.js.map +1 -1
  8. package/out-tsc/src/chat/Chat.js +29 -7
  9. package/out-tsc/src/chat/Chat.js.map +1 -1
  10. package/out-tsc/src/compose/Compose.js +16 -20
  11. package/out-tsc/src/compose/Compose.js.map +1 -1
  12. package/out-tsc/src/contacts/ContactChat.js +240 -114
  13. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  14. package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
  15. package/out-tsc/src/contacts/events.js.map +1 -1
  16. package/out-tsc/src/contacts/helpers.js +5 -1
  17. package/out-tsc/src/contacts/helpers.js.map +1 -1
  18. package/out-tsc/src/contactsearch/ContactSearch.js +1 -1
  19. package/out-tsc/src/contactsearch/ContactSearch.js.map +1 -1
  20. package/out-tsc/src/dropdown/Dropdown.js +121 -108
  21. package/out-tsc/src/dropdown/Dropdown.js.map +1 -1
  22. package/out-tsc/src/interfaces.js +2 -0
  23. package/out-tsc/src/interfaces.js.map +1 -1
  24. package/out-tsc/src/list/ContentMenu.js +11 -8
  25. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  26. package/out-tsc/src/list/RunList.js.map +1 -1
  27. package/out-tsc/src/list/TembaList.js +21 -14
  28. package/out-tsc/src/list/TembaList.js.map +1 -1
  29. package/out-tsc/src/list/TembaMenu.js +11 -12
  30. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  31. package/out-tsc/src/list/TicketList.js +10 -0
  32. package/out-tsc/src/list/TicketList.js.map +1 -1
  33. package/out-tsc/src/omnibox/Omnibox.js +33 -90
  34. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  35. package/out-tsc/src/options/Options.js +49 -47
  36. package/out-tsc/src/options/Options.js.map +1 -1
  37. package/out-tsc/src/select/PopupSelect.js +57 -0
  38. package/out-tsc/src/select/PopupSelect.js.map +1 -0
  39. package/out-tsc/src/select/Select.js +194 -144
  40. package/out-tsc/src/select/Select.js.map +1 -1
  41. package/out-tsc/src/select/UserSelect.js +67 -0
  42. package/out-tsc/src/select/UserSelect.js.map +1 -0
  43. package/out-tsc/src/store/Store.js +65 -14
  44. package/out-tsc/src/store/Store.js.map +1 -1
  45. package/out-tsc/src/tabpane/TabPane.js +82 -115
  46. package/out-tsc/src/tabpane/TabPane.js.map +1 -1
  47. package/out-tsc/src/textinput/TextInput.js +1 -0
  48. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  49. package/out-tsc/src/user/TembaUser.js +24 -37
  50. package/out-tsc/src/user/TembaUser.js.map +1 -1
  51. package/out-tsc/src/utils/index.js +13 -6
  52. package/out-tsc/src/utils/index.js.map +1 -1
  53. package/out-tsc/temba-modules.js +4 -2
  54. package/out-tsc/temba-modules.js.map +1 -1
  55. package/out-tsc/test/temba-omnibox.test.js +43 -4
  56. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  57. package/out-tsc/test/temba-select.test.js +121 -65
  58. package/out-tsc/test/temba-select.test.js.map +1 -1
  59. package/out-tsc/test/utils.test.js +4 -0
  60. package/out-tsc/test/utils.test.js.map +1 -1
  61. package/package.json +1 -1
  62. package/screenshots/truth/compose/attachments-tab.png +0 -0
  63. package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
  64. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  65. package/screenshots/truth/compose/intial-text.png +0 -0
  66. package/screenshots/truth/compose/no-counter.png +0 -0
  67. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  68. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  69. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  70. package/screenshots/truth/contacts/chat-failure.png +0 -0
  71. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  72. package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
  73. package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
  74. package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
  75. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  76. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  77. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  78. package/screenshots/truth/content-menu/item-no-buttons.png +0 -0
  79. package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
  80. package/screenshots/truth/omnibox/selected.png +0 -0
  81. package/screenshots/truth/select/enabled-multi-selection.png +0 -0
  82. package/screenshots/truth/select/endpoint-initial-value-updated.png +0 -0
  83. package/screenshots/truth/select/endpoint-initial-value.png +0 -0
  84. package/screenshots/truth/select/expressions.png +0 -0
  85. package/screenshots/truth/select/functions.png +0 -0
  86. package/screenshots/truth/select/initial-value.png +0 -0
  87. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  88. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  89. package/screenshots/truth/select/selected-multi-test.png +0 -0
  90. package/screenshots/truth/select/static-initial-value.png +0 -0
  91. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  92. package/screenshots/truth/select/value-initial.png +0 -0
  93. package/src/aliaseditor/AliasEditor.ts +1 -1
  94. package/src/button/Button.ts +6 -2
  95. package/src/chat/Chat.ts +28 -6
  96. package/src/compose/Compose.ts +17 -22
  97. package/src/contacts/ContactChat.ts +260 -118
  98. package/src/contacts/ContactFieldEditor.ts +1 -1
  99. package/src/contacts/events.ts +1 -0
  100. package/src/contacts/helpers.ts +8 -1
  101. package/src/contactsearch/ContactSearch.ts +3 -3
  102. package/src/dropdown/Dropdown.ts +142 -103
  103. package/src/interfaces.ts +4 -1
  104. package/src/list/ContentMenu.ts +11 -9
  105. package/src/list/RunList.ts +3 -1
  106. package/src/list/TembaList.ts +24 -14
  107. package/src/list/TembaMenu.ts +14 -15
  108. package/src/list/TicketList.ts +11 -0
  109. package/src/omnibox/Omnibox.ts +34 -95
  110. package/src/options/Options.ts +57 -60
  111. package/src/select/PopupSelect.ts +53 -0
  112. package/src/select/Select.ts +182 -112
  113. package/src/select/UserSelect.ts +71 -0
  114. package/src/store/Store.ts +70 -21
  115. package/src/tabpane/TabPane.ts +91 -113
  116. package/src/textinput/TextInput.ts +1 -0
  117. package/src/user/TembaUser.ts +30 -41
  118. package/src/utils/index.ts +12 -8
  119. package/temba-modules.ts +4 -2
  120. package/test/temba-omnibox.test.ts +56 -4
  121. package/test/temba-select.test.ts +170 -56
  122. package/test/utils.test.ts +5 -0
  123. package/test-assets/select/omnibox.json +55 -0
  124. package/web-test-runner.config.mjs +16 -4
  125. package/out-tsc/src/contacts/ContactTickets.js +0 -462
  126. package/out-tsc/src/contacts/ContactTickets.js.map +0 -1
  127. package/out-tsc/test/temba-contact-tickets.test.js +0 -36
  128. package/out-tsc/test/temba-contact-tickets.test.js.map +0 -1
  129. package/src/contacts/ContactTickets.ts +0 -490
  130. package/test/temba-contact-tickets.test.ts +0 -52
@@ -1,42 +1,42 @@
1
+ import * as sinon from 'sinon';
1
2
  import { fixture, expect, assert } from '@open-wc/testing';
2
3
  import { useFakeTimers } from 'sinon';
3
4
  import { Options } from '../src/options/Options';
4
- import { Select } from '../src/select/Select';
5
+ import { Select, SelectOption } from '../src/select/Select';
5
6
  import {
6
7
  assertScreenshot,
7
8
  checkTimers,
8
9
  getClip,
9
- loadStore
10
+ loadStore,
11
+ mouseClickElement
10
12
  } from './utils.test';
11
13
  import { CustomEventType } from '../src/interfaces';
12
14
 
13
- let clock: any;
14
-
15
15
  const colors = [
16
16
  { name: 'Red', value: '0' },
17
17
  { name: 'Green', value: '1' },
18
18
  { name: 'Blue', value: '2' }
19
19
  ];
20
20
 
21
- export const createSelect = async (def: string) => {
21
+ export const createSelect = async (clock, def: string) => {
22
22
  const parentNode = document.createElement('div');
23
23
  parentNode.setAttribute('style', 'width: 250px;');
24
24
 
25
- const select: Select = await fixture(def, { parentNode });
25
+ const select: Select<SelectOption> = await fixture(def, { parentNode });
26
26
  clock.runAll();
27
27
  await select.updateComplete;
28
28
  return select;
29
29
  };
30
30
 
31
- export const open = async (select: Select) => {
31
+ export const open = async (clock, select: Select<SelectOption>) => {
32
32
  if (!select.endpoint) {
33
- await click('temba-select');
33
+ await mouseClickElement(select);
34
34
  await clock.runAll();
35
35
  await clock.runAll();
36
36
  return select;
37
37
  }
38
38
 
39
- const promise = new Promise<Select>((resolve) => {
39
+ const promise = new Promise<Select<SelectOption>>((resolve) => {
40
40
  select.addEventListener(
41
41
  CustomEventType.FetchComplete,
42
42
  async () => {
@@ -47,26 +47,31 @@ export const open = async (select: Select) => {
47
47
  );
48
48
  });
49
49
 
50
- await click('temba-select');
50
+ await mouseClickElement(select);
51
51
  await clock.runAll();
52
52
 
53
53
  return promise;
54
54
  };
55
55
 
56
- export const clear = (select: Select) => {
56
+ export const clear = (select: Select<SelectOption>) => {
57
57
  (select.shadowRoot.querySelector('.clear-button') as HTMLDivElement).click();
58
58
  };
59
59
 
60
- export const getOptions = (select: Select): Options => {
60
+ export const getOptions = (select: Select<SelectOption>): Options => {
61
61
  return select.shadowRoot.querySelector('temba-options[visible]');
62
62
  };
63
63
 
64
- export const clickOption = async (select: Select, index: number) => {
64
+ export const clickOption = async (
65
+ clock: any,
66
+ select: Select<SelectOption>,
67
+ index: number
68
+ ) => {
65
69
  const options = getOptions(select);
66
70
  const option = options.shadowRoot.querySelector(
67
71
  `[data-option-index="${index}"]`
68
72
  ) as HTMLDivElement;
69
- option.click();
73
+
74
+ await mouseClickElement(option);
70
75
  await options.updateComplete;
71
76
  await select.updateComplete;
72
77
  await clock.runAll();
@@ -74,30 +79,48 @@ export const clickOption = async (select: Select, index: number) => {
74
79
  checkTimers(clock);
75
80
  };
76
81
 
77
- export const openAndClick = async (select: Select, index: number) => {
78
- await open(select);
79
- await clickOption(select, index);
82
+ export const openAndClick = async (
83
+ clock: any,
84
+ select: Select<SelectOption>,
85
+ index: number
86
+ ) => {
87
+ await open(clock, select);
88
+ await clickOption(clock, select, index);
80
89
  };
81
90
 
82
91
  export const getSelectHTML = (
83
- options: any[] = colors,
84
- attrs: any = { placeholder: 'Select a color', name: 'color' }
92
+ options: SelectOption[] = colors,
93
+ attrs: any = { placeholder: 'Select a color', name: 'color' },
94
+ selected: any = null
85
95
  ): string => {
86
96
  const selectHTML = `
87
- <temba-select ${Object.keys(attrs)
88
- .map((name: string) => `${name}='${attrs[name]}'`)
97
+ <temba-select${Object.keys(attrs)
98
+ .map((name: string) => {
99
+ // check if it's a string attribute
100
+ if (typeof attrs[name] === 'string') {
101
+ return ` ${name}="${attrs[name].replace(/"/g, '&quot;')}"`;
102
+ }
103
+
104
+ if (typeof attrs[name] === 'boolean') {
105
+ return ` ${name}`;
106
+ }
107
+
108
+ return ` ${name}="${attrs[name]}"`;
109
+ })
89
110
  .join(' ')}>
90
111
  ${options
91
112
  .map(
92
113
  (option) =>
93
- `<temba-option name="${option.name}" value="${option.value}"></temba-option>`
114
+ `<temba-option name="${option.name}" value="${option.value}"${
115
+ option.selected || option.value === selected ? ' selected' : ''
116
+ }></temba-option>`
94
117
  )
95
118
  .join('')}
96
119
  </temba-select>`;
97
120
  return selectHTML;
98
121
  };
99
122
 
100
- const getClipWithOptions = (select: Select) => {
123
+ const getClipWithOptions = (select: Select<any>) => {
101
124
  const selectClip = getClip(select);
102
125
  const options = select.shadowRoot.querySelector(
103
126
  'temba-options[visible]'
@@ -120,9 +143,9 @@ const getClipWithOptions = (select: Select) => {
120
143
  };
121
144
 
122
145
  describe('temba-select', () => {
146
+ let clock: any;
123
147
  beforeEach(function () {
124
148
  clock = useFakeTimers();
125
-
126
149
  clock.tick(400);
127
150
  setViewport({ width: 500, height: 1000, deviceScaleFactor: 2 });
128
151
  });
@@ -132,12 +155,13 @@ describe('temba-select', () => {
132
155
  });
133
156
 
134
157
  it('can be created', async () => {
135
- const select = await createSelect('<temba-select></temba-select>');
158
+ const select = await createSelect(clock, '<temba-select></temba-select>');
136
159
  assert.instanceOf(select, Select);
137
160
  });
138
161
 
139
162
  it('can be disabled', async () => {
140
163
  const select = await createSelect(
164
+ clock,
141
165
  getSelectHTML(colors, { disabled: true })
142
166
  );
143
167
 
@@ -147,6 +171,7 @@ describe('temba-select', () => {
147
171
 
148
172
  it('can be disabled with selection', async () => {
149
173
  const select = await createSelect(
174
+ clock,
150
175
  getSelectHTML(colors, { disabled: true, value: '0' })
151
176
  );
152
177
 
@@ -156,36 +181,37 @@ describe('temba-select', () => {
156
181
 
157
182
  it('can be disabled with multi selection', async () => {
158
183
  const select = await createSelect(
184
+ clock,
159
185
  getSelectHTML(colors, { placeholder: 'Select a color', multi: true })
160
186
  );
161
187
 
162
- await openAndClick(select, 0);
188
+ await openAndClick(clock, select, 0);
163
189
  select.disabled = true;
164
190
  expect(select.disabled).to.equal(true);
165
191
 
166
192
  // make sure we can't select anymore
167
- await open(select);
193
+ await open(clock, select);
168
194
  expect(select.isOpen()).to.equal(false);
169
195
  await assertScreenshot('select/disabled-multi-selection', getClip(select));
170
196
  });
171
197
 
172
198
  it('can be created with temba-option tags', async () => {
173
- const select = await createSelect(getSelectHTML());
199
+ const select = await createSelect(clock, getSelectHTML());
174
200
  assert.equal(select.getStaticOptions().length, 3);
175
201
  expect(select.values.length).to.equal(0);
176
202
  await assertScreenshot('select/with-placeholder', getClip(select));
177
203
  });
178
204
 
179
205
  it('picks the first option without a placeholder', async () => {
180
- const select = await createSelect(getSelectHTML(colors, {}));
206
+ const select = await createSelect(clock, getSelectHTML(colors, {}));
181
207
  assert.equal(select.getStaticOptions().length, 3);
182
208
  expect(select.values[0].name).to.equal('Red');
183
209
  await assertScreenshot('select/without-placeholder', getClip(select));
184
210
  });
185
211
 
186
212
  it('shows options when opened', async () => {
187
- const select = await createSelect(getSelectHTML());
188
- await open(select);
213
+ const select = await createSelect(clock, getSelectHTML());
214
+ await open(clock, select);
189
215
  const options = getOptions(select);
190
216
  assert.instanceOf(options, Options);
191
217
 
@@ -201,21 +227,27 @@ describe('temba-select', () => {
201
227
 
202
228
  it('can be created with attribute options', async () => {
203
229
  const options = JSON.stringify([{ name: 'Embedded Option', value: '0' }]);
204
- const select = await createSelect(getSelectHTML([], { options }));
230
+ const select = await createSelect(clock, getSelectHTML([], { options }));
205
231
  // select the first option
206
- await openAndClick(select, 0);
232
+ await openAndClick(clock, select, 0);
207
233
  expect(select.values[0].name).to.equal('Embedded Option');
208
234
  await assertScreenshot('select/embedded', getClipWithOptions(select));
209
235
  });
210
236
 
211
237
  describe('single selection', () => {
212
238
  it('can select a single option', async () => {
213
- const select = await createSelect(getSelectHTML());
239
+ const select = await createSelect(clock, getSelectHTML());
240
+
241
+ // nothing is selected to start
214
242
  expect(select.values.length).to.equal(0);
243
+ expect(select.value).to.equal(null);
215
244
 
216
245
  // select the first option
217
- await openAndClick(select, 0);
246
+ const changeEvent = sinon.spy();
247
+ select.addEventListener('change', changeEvent);
248
+ await openAndClick(clock, select, 0);
218
249
 
250
+ assert(changeEvent.called, 'change event not fired');
219
251
  expect(select.values.length).to.equal(1);
220
252
  expect(select.values[0].name).to.equal('Red');
221
253
  expect(select.shadowRoot.innerHTML).to.contain('Red');
@@ -228,22 +260,23 @@ describe('temba-select', () => {
228
260
 
229
261
  it('can search with existing selection', async () => {
230
262
  const select = await createSelect(
263
+ clock,
231
264
  getSelectHTML(colors, { searchable: true })
232
265
  );
233
266
 
234
267
  // select the second option
235
- await openAndClick(select, 1);
268
+ await openAndClick(clock, select, 1);
236
269
  expect(select.values.length).to.equal(1);
237
270
  expect(select.values[0].name).to.equal('Green');
238
271
 
239
272
  // for single selection our current selection should be in the list and focused
240
- await open(select);
273
+ await open(clock, select);
241
274
  assert.equal(select.cursorIndex, 1);
242
275
  assert.equal(select.visibleOptions.length, 3);
243
276
 
244
277
  // now lets do a search, we should see our selection (green) and one other (red)
245
278
  await typeInto('temba-select', 're', false);
246
- await open(select);
279
+ await open(clock, select);
247
280
  assert.equal(select.visibleOptions.length, 2);
248
281
 
249
282
  await assertScreenshot(
@@ -259,13 +292,21 @@ describe('temba-select', () => {
259
292
  describe('multiple selection', () => {
260
293
  it('can select multiple options', async () => {
261
294
  const select = await createSelect(
295
+ clock,
262
296
  getSelectHTML(colors, { placeholder: 'Select a color', multi: true })
263
297
  );
264
298
  expect(select.values.length).to.equal(0);
265
299
 
300
+ const changeEvent = sinon.spy();
301
+ select.addEventListener('change', changeEvent);
302
+
266
303
  // select the first option twice
267
- await openAndClick(select, 0);
268
- await openAndClick(select, 0);
304
+ await openAndClick(clock, select, 0);
305
+ assert(changeEvent.called, 'change event not fired');
306
+
307
+ changeEvent.resetHistory();
308
+ await openAndClick(clock, select, 0);
309
+ assert(changeEvent.called, 'change event not fired');
269
310
 
270
311
  // now we should have red and green selected
271
312
  expect(select.values.length).to.equal(2);
@@ -277,18 +318,58 @@ describe('temba-select', () => {
277
318
  getClipWithOptions(select)
278
319
  );
279
320
  });
321
+
322
+ it('shows multiple values on initialization', async () => {
323
+ const select = await createSelect(
324
+ clock,
325
+ getSelectHTML(
326
+ [
327
+ { name: 'Red', value: '0' },
328
+ { name: 'Green', value: '1', selected: true },
329
+ { name: 'Blue', value: '2', selected: true }
330
+ ],
331
+ {
332
+ placeholder: 'Select a color',
333
+ multi: true
334
+ }
335
+ )
336
+ );
337
+ await assertScreenshot('select/multiple-initial-values', getClip(select));
338
+ expect(select.values.length).to.equal(2);
339
+ });
340
+ });
341
+
342
+ describe('static options', () => {
343
+ it('accepts an initial value', async () => {
344
+ const select = await createSelect(
345
+ clock,
346
+ getSelectHTML(colors, { value: '1' })
347
+ );
348
+ expect(select.values[0].name).to.equal('Green');
349
+ await assertScreenshot('select/static-initial-value', getClip(select));
350
+ });
351
+
352
+ it('honors temba-option selected attribute', async () => {
353
+ const select = await createSelect(clock, getSelectHTML(colors, {}, '1'));
354
+ expect(select.values[0].name).to.equal('Green');
355
+ await assertScreenshot(
356
+ 'select/static-initial-via-selected',
357
+ getClip(select)
358
+ );
359
+ });
280
360
  });
281
361
 
282
362
  describe('endpoints', () => {
283
363
  it('can load from an endpoint', async () => {
284
364
  const select = await createSelect(
365
+ clock,
285
366
  getSelectHTML([], {
286
367
  placeholder: 'Select a color',
287
368
  endpoint: '/test-assets/select/colors.json'
288
369
  })
289
370
  );
290
371
 
291
- await open(select);
372
+ await open(clock, select);
292
373
  await assertScreenshot(
293
374
  'select/remote-options',
294
375
  getClipWithOptions(select)
@@ -298,6 +379,7 @@ describe('temba-select', () => {
298
379
 
299
380
  it('can search an endpoint', async () => {
300
381
  const select = await createSelect(
382
+ clock,
301
383
  getSelectHTML([], {
302
384
  placeholder: 'Select a color',
303
385
  endpoint: '/test-assets/select/colors.json',
@@ -306,14 +388,36 @@ describe('temba-select', () => {
306
388
  );
307
389
 
308
390
  await typeInto('temba-select', 're', false);
309
- await open(select);
391
+ await open(clock, select);
310
392
  assert.equal(select.visibleOptions.length, 2);
311
393
 
312
394
  await assertScreenshot('select/searching', getClipWithOptions(select));
313
395
  });
314
396
 
397
+ it('can use an endpoint and allow multiple', async () => {
398
+ const select = await createSelect(
399
+ clock,
400
+ getSelectHTML([], {
401
+ placeholder: 'Select a color',
402
+ endpoint: '/test-assets/select/colors.json',
403
+ searchable: true,
404
+ multi: true
405
+ })
406
+ );
407
+
408
+ await assertScreenshot(
409
+ 'select/multi-with-endpoint',
410
+ getClipWithOptions(select)
411
+ );
412
+
413
+ // await typeInto('temba-select', 're', false);
414
+ // await open(select);
415
+ // assert.equal(select.visibleOptions.length, 2);
416
+ });
417
+
315
418
  it('pages through cursor results', async () => {
316
419
  const select = await createSelect(
420
+ clock,
317
421
  getSelectHTML([], {
318
422
  placeholder: 'Select a group',
319
423
  endpoint: '/test-assets/select/groups.json',
@@ -321,7 +425,7 @@ describe('temba-select', () => {
321
425
  })
322
426
  );
323
427
 
324
- await open(select);
428
+ await open(clock, select);
325
429
 
326
430
  // should have all three pages visible right away
327
431
  assert.equal(select.visibleOptions.length, 15);
@@ -329,6 +433,7 @@ describe('temba-select', () => {
329
433
 
330
434
  it('shows cached results', async () => {
331
435
  const select = await createSelect(
436
+ clock,
332
437
  getSelectHTML([], {
333
438
  placeholder: 'Select a group',
334
439
  endpoint: '/test-assets/select/groups.json',
@@ -338,14 +443,14 @@ describe('temba-select', () => {
338
443
  );
339
444
 
340
445
  // wait for updates from fetching three pages
341
- await open(select);
446
+ await open(clock, select);
342
447
  assert.equal(select.visibleOptions.length, 15);
343
448
 
344
449
  // close and reopen
345
450
  select.blur();
346
451
  await clock.tick(250);
347
452
 
348
- await open(select);
453
+ await open(clock, select);
349
454
  assert.equal(select.visibleOptions.length, 15);
350
455
 
351
456
  // close and reopen once more (previous bug failed on third opening)
@@ -357,6 +462,7 @@ describe('temba-select', () => {
357
462
  it('can enter expressions', async () => {
358
463
  await loadStore();
359
464
  const select = await createSelect(
465
+ clock,
360
466
  getSelectHTML([], {
361
467
  endpoint: '/colors.json',
362
468
  searchable: true,
@@ -365,7 +471,7 @@ describe('temba-select', () => {
365
471
  );
366
472
 
367
473
  await typeInto('temba-select', 'Hi there @contact', false);
368
- await open(select);
474
+ await open(clock, select);
369
475
 
370
476
  assert.equal(select.completionOptions.length, 14);
371
477
  await assertScreenshot('select/expressions', getClipWithOptions(select));
@@ -373,11 +479,12 @@ describe('temba-select', () => {
373
479
 
374
480
  it('clears single selection', async () => {
375
481
  const select = await createSelect(
482
+ clock,
376
483
  getSelectHTML(colors, { clearable: true })
377
484
  );
378
485
  assert.equal(select.getStaticOptions().length, 3);
379
486
 
380
- await openAndClick(select, 0);
487
+ await openAndClick(clock, select, 0);
381
488
  expect(select.values[0].name).to.equal('Red');
382
489
 
383
490
  await assertScreenshot('select/selection-clearable', getClip(select));
@@ -388,6 +495,7 @@ describe('temba-select', () => {
388
495
 
389
496
  it('should look the same with search enabled', async () => {
390
497
  const select = await createSelect(
498
+ clock,
391
499
  getSelectHTML(colors, {
392
500
  placeholder: 'Select a color',
393
501
  searchable: true
@@ -401,11 +509,12 @@ describe('temba-select', () => {
401
509
 
402
510
  it('should look the same with search enabled and selection made', async () => {
403
511
  const select = await createSelect(
512
+ clock,
404
513
  getSelectHTML(colors, { searchable: true })
405
514
  );
406
515
 
407
516
  // select the first option
408
- await openAndClick(select, 1);
517
+ await openAndClick(clock, select, 1);
409
518
  await assertScreenshot(
410
519
  'select/search-selected',
411
520
  getClipWithOptions(select)
@@ -414,14 +523,15 @@ describe('temba-select', () => {
414
523
 
415
524
  it('should show focus for the selected option', async () => {
416
525
  const select = await createSelect(
526
+ clock,
417
527
  getSelectHTML(colors, { searchable: true })
418
528
  );
419
529
 
420
530
  // select the first option
421
- await openAndClick(select, 1);
531
+ await openAndClick(clock, select, 1);
422
532
 
423
533
  // now open and look at focus
424
- await open(select);
534
+ await open(clock, select);
425
535
  await assertScreenshot(
426
536
  'select/search-selected-focus',
427
537
  getClipWithOptions(select)
@@ -430,6 +540,7 @@ describe('temba-select', () => {
430
540
 
431
541
  it('should show search with existing multiple selection', async () => {
432
542
  const select = await createSelect(
543
+ clock,
433
544
  getSelectHTML(colors, {
434
545
  placeholder: 'Select a color',
435
546
  searchable: true,
@@ -438,13 +549,13 @@ describe('temba-select', () => {
438
549
  );
439
550
 
440
551
  // select the first option
441
- await openAndClick(select, 0);
442
- await openAndClick(select, 0);
443
- await open(select);
552
+ await openAndClick(clock, select, 0);
553
+ await openAndClick(clock, select, 0);
554
+ await open(clock, select);
444
555
 
445
556
  // now lets do a search, we should see our selection (green) and one other (red)
446
557
  await typeInto('temba-select', 're', false);
447
- await open(select);
558
+ await open(clock, select);
448
559
 
449
560
  // should have two things selected and active query and no matching options
450
561
  await assertScreenshot(
@@ -457,6 +568,7 @@ describe('temba-select', () => {
457
568
  await loadStore();
458
569
 
459
570
  const select = await createSelect(
571
+ clock,
460
572
  getSelectHTML(colors, {
461
573
  placeholder: 'Select a color',
462
574
  searchable: true,
@@ -465,7 +577,7 @@ describe('temba-select', () => {
465
577
  );
466
578
 
467
579
  await typeInto('temba-select', 'look at @(max(m', false);
468
- await open(select);
580
+ await open(clock, select);
469
581
 
470
582
  await assertScreenshot('select/functions', getClipWithOptions(select));
471
583
  });
@@ -479,6 +591,7 @@ describe('temba-select', () => {
479
591
  ];
480
592
 
481
593
  const select = await createSelect(
594
+ clock,
482
595
  getSelectHTML(options, {
483
596
  value: '0'
484
597
  })
@@ -494,6 +607,7 @@ describe('temba-select', () => {
494
607
  await loadStore();
495
608
 
496
609
  const select = await createSelect(
610
+ clock,
497
611
  getSelectHTML(colors, {
498
612
  multi: true,
499
613
  placeholder: 'Select a color',
@@ -503,7 +617,7 @@ describe('temba-select', () => {
503
617
  );
504
618
 
505
619
  await typeInto('temba-select', '@con', false);
506
- await openAndClick(select, 0);
620
+ await openAndClick(clock, select, 0);
507
621
 
508
622
  expect(select.values[0].name).to.equal('@contact');
509
623
  await assertScreenshot(
@@ -239,6 +239,11 @@ export const getClip = (ele: HTMLElement) => {
239
239
  return newClip;
240
240
  };
241
241
 
242
+ export const mouseClickElement = async (ele: HTMLElement) => {
243
+ const bounds = ele.getBoundingClientRect();
244
+ await mouseClick(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
245
+ };
246
+
242
247
  export const getHTMLAttrs = (attrs: any = {}) => {
243
248
  return Object.keys(attrs)
244
249
  .map((name: string) => `${name}='${attrs[name]}'`)
@@ -0,0 +1,55 @@
1
+ {
2
+ "results": [
3
+ {
4
+ "id": "47cf9575-a326-468b-a26d-8c0577197426",
5
+ "name": "Doctors",
6
+ "type": "group",
7
+ "count": 133
8
+ },
9
+ {
10
+ "id": "0dea1b4b-29bb-4727-8c84-a70216dbf3e1",
11
+ "name": "Drivers",
12
+ "type": "group",
13
+ "count": 62
14
+ },
15
+ {
16
+ "id": "27099c42-a3db-4a82-8052-8530062e4eeb",
17
+ "name": "Empty",
18
+ "type": "group",
19
+ "count": 0
20
+ },
21
+ {
22
+ "id": "92405e93-5db1-4dd5-bfc4-90e9f4d74627",
23
+ "name": "Farmers",
24
+ "type": "group",
25
+ "count": 166
26
+ },
27
+ {
28
+ "id": "adb5285c-8c6c-4d96-8c5b-4684468e2ef4",
29
+ "name": "Open Tickets",
30
+ "type": "group",
31
+ "count": 3
32
+ },
33
+ {
34
+ "id": "6b21b63c-cc31-4314-bda6-b4a864ddf681",
35
+ "name": "Reporters",
36
+ "type": "group",
37
+ "count": 306
38
+ },
39
+ {
40
+ "id": "9c431fb2-eee4-4200-91f9-6188f377b80e",
41
+ "name": "Teachers",
42
+ "type": "group",
43
+ "count": 105
44
+ },
45
+ {
46
+ "id": "827d8d18-07a1-48da-8146-965b9d6a59c8",
47
+ "name": "Testers",
48
+ "type": "group",
49
+ "count": 31
50
+ }
51
+ ],
52
+ "more": false,
53
+ "total": 8,
54
+ "err": "nil"
55
+ }