@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.
- package/CHANGELOG.md +27 -2
- package/demo/index.html +1 -1
- package/dist/temba-components.js +794 -968
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/aliaseditor/AliasEditor.js.map +1 -1
- package/out-tsc/src/button/Button.js +6 -2
- package/out-tsc/src/button/Button.js.map +1 -1
- package/out-tsc/src/chat/Chat.js +29 -7
- package/out-tsc/src/chat/Chat.js.map +1 -1
- package/out-tsc/src/compose/Compose.js +16 -20
- package/out-tsc/src/compose/Compose.js.map +1 -1
- package/out-tsc/src/contacts/ContactChat.js +240 -114
- package/out-tsc/src/contacts/ContactChat.js.map +1 -1
- package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
- package/out-tsc/src/contacts/events.js.map +1 -1
- package/out-tsc/src/contacts/helpers.js +5 -1
- package/out-tsc/src/contacts/helpers.js.map +1 -1
- package/out-tsc/src/contactsearch/ContactSearch.js +1 -1
- package/out-tsc/src/contactsearch/ContactSearch.js.map +1 -1
- package/out-tsc/src/dropdown/Dropdown.js +121 -108
- package/out-tsc/src/dropdown/Dropdown.js.map +1 -1
- package/out-tsc/src/interfaces.js +2 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/ContentMenu.js +11 -8
- package/out-tsc/src/list/ContentMenu.js.map +1 -1
- package/out-tsc/src/list/RunList.js.map +1 -1
- package/out-tsc/src/list/TembaList.js +21 -14
- package/out-tsc/src/list/TembaList.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +11 -12
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/list/TicketList.js +10 -0
- package/out-tsc/src/list/TicketList.js.map +1 -1
- package/out-tsc/src/omnibox/Omnibox.js +33 -90
- package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
- package/out-tsc/src/options/Options.js +49 -47
- package/out-tsc/src/options/Options.js.map +1 -1
- package/out-tsc/src/select/PopupSelect.js +57 -0
- package/out-tsc/src/select/PopupSelect.js.map +1 -0
- package/out-tsc/src/select/Select.js +194 -144
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/select/UserSelect.js +67 -0
- package/out-tsc/src/select/UserSelect.js.map +1 -0
- package/out-tsc/src/store/Store.js +65 -14
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/tabpane/TabPane.js +82 -115
- package/out-tsc/src/tabpane/TabPane.js.map +1 -1
- package/out-tsc/src/textinput/TextInput.js +1 -0
- package/out-tsc/src/textinput/TextInput.js.map +1 -1
- package/out-tsc/src/user/TembaUser.js +24 -37
- package/out-tsc/src/user/TembaUser.js.map +1 -1
- package/out-tsc/src/utils/index.js +13 -6
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/temba-modules.js +4 -2
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-omnibox.test.js +43 -4
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +121 -65
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +4 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/compose/attachments-tab.png +0 -0
- package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
- package/screenshots/truth/compose/attachments-with-files.png +0 -0
- package/screenshots/truth/compose/intial-text.png +0 -0
- package/screenshots/truth/compose/no-counter.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
- package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
- package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
- package/screenshots/truth/contacts/chat-failure.png +0 -0
- package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
- package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
- package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
- package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
- package/screenshots/truth/content-menu/item-no-buttons.png +0 -0
- package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/select/enabled-multi-selection.png +0 -0
- package/screenshots/truth/select/endpoint-initial-value-updated.png +0 -0
- package/screenshots/truth/select/endpoint-initial-value.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/initial-value.png +0 -0
- package/screenshots/truth/select/multi-with-endpoint.png +0 -0
- package/screenshots/truth/select/multiple-initial-values.png +0 -0
- package/screenshots/truth/select/selected-multi-test.png +0 -0
- package/screenshots/truth/select/static-initial-value.png +0 -0
- package/screenshots/truth/select/static-initial-via-selected.png +0 -0
- package/screenshots/truth/select/value-initial.png +0 -0
- package/src/aliaseditor/AliasEditor.ts +1 -1
- package/src/button/Button.ts +6 -2
- package/src/chat/Chat.ts +28 -6
- package/src/compose/Compose.ts +17 -22
- package/src/contacts/ContactChat.ts +260 -118
- package/src/contacts/ContactFieldEditor.ts +1 -1
- package/src/contacts/events.ts +1 -0
- package/src/contacts/helpers.ts +8 -1
- package/src/contactsearch/ContactSearch.ts +3 -3
- package/src/dropdown/Dropdown.ts +142 -103
- package/src/interfaces.ts +4 -1
- package/src/list/ContentMenu.ts +11 -9
- package/src/list/RunList.ts +3 -1
- package/src/list/TembaList.ts +24 -14
- package/src/list/TembaMenu.ts +14 -15
- package/src/list/TicketList.ts +11 -0
- package/src/omnibox/Omnibox.ts +34 -95
- package/src/options/Options.ts +57 -60
- package/src/select/PopupSelect.ts +53 -0
- package/src/select/Select.ts +182 -112
- package/src/select/UserSelect.ts +71 -0
- package/src/store/Store.ts +70 -21
- package/src/tabpane/TabPane.ts +91 -113
- package/src/textinput/TextInput.ts +1 -0
- package/src/user/TembaUser.ts +30 -41
- package/src/utils/index.ts +12 -8
- package/temba-modules.ts +4 -2
- package/test/temba-omnibox.test.ts +56 -4
- package/test/temba-select.test.ts +170 -56
- package/test/utils.test.ts +5 -0
- package/test-assets/select/omnibox.json +55 -0
- package/web-test-runner.config.mjs +16 -4
- package/out-tsc/src/contacts/ContactTickets.js +0 -462
- package/out-tsc/src/contacts/ContactTickets.js.map +0 -1
- package/out-tsc/test/temba-contact-tickets.test.js +0 -36
- package/out-tsc/test/temba-contact-tickets.test.js.map +0 -1
- package/src/contacts/ContactTickets.ts +0 -490
- 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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
78
|
-
|
|
79
|
-
|
|
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:
|
|
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
|
|
88
|
-
.map((name: string) =>
|
|
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, '"')}"`;
|
|
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}"
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
package/test/utils.test.ts
CHANGED
|
@@ -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
|
+
}
|