@nyaruka/temba-components 0.123.0 → 0.124.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/.github/copilot-instructions.md +22 -4
- package/CHANGELOG.md +11 -0
- package/demo/drag-drop-demo.html +141 -0
- package/demo/index.html +15 -0
- package/demo/test-drag-drop.html +94 -0
- package/dist/temba-components.js +323 -191
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/fields/FieldManager.js +27 -34
- package/out-tsc/src/fields/FieldManager.js.map +1 -1
- package/out-tsc/src/list/SortableList.js +257 -60
- package/out-tsc/src/list/SortableList.js.map +1 -1
- package/out-tsc/src/omnibox/Omnibox.js +1 -1
- package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
- package/out-tsc/src/select/Select.js +198 -38
- package/out-tsc/src/select/Select.js.map +1 -1
- package/out-tsc/src/webchat/WebChat.js +5 -2
- package/out-tsc/src/webchat/WebChat.js.map +1 -1
- package/out-tsc/test/temba-flow-editor-node.test.js +273 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor.test.js +244 -0
- package/out-tsc/test/temba-flow-editor.test.js.map +1 -0
- package/out-tsc/test/temba-flow-plumber.test.js +145 -0
- package/out-tsc/test/temba-flow-plumber.test.js.map +1 -0
- package/out-tsc/test/temba-flow-render.test.js +171 -0
- package/out-tsc/test/temba-flow-render.test.js.map +1 -0
- package/out-tsc/test/temba-omnibox.test.js +2 -3
- package/out-tsc/test/temba-omnibox.test.js.map +1 -1
- package/out-tsc/test/temba-select.test.js +134 -53
- package/out-tsc/test/temba-select.test.js.map +1 -1
- package/out-tsc/test/temba-sortable-list.test.js +91 -15
- package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
- package/out-tsc/test/temba-webchat-lightbox-fix.test.js +42 -0
- package/out-tsc/test/temba-webchat-lightbox-fix.test.js.map +1 -0
- package/out-tsc/test/utils.test.js +30 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/flow/editor-basic.png +0 -0
- package/screenshots/truth/list/fields-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dragging.png +0 -0
- package/screenshots/truth/list/sortable-dropped.png +0 -0
- package/screenshots/truth/list/sortable.png +0 -0
- package/screenshots/truth/omnibox/selected.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/empty-options.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/multi-reorder-final.png +0 -0
- package/screenshots/truth/select/multi-reorder-initial.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/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/selection-clearable.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/truncated-selection.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/screenshots/truth/templates/default.png +0 -0
- package/screenshots/truth/templates/unapproved.png +0 -0
- package/src/fields/FieldManager.ts +30 -38
- package/src/list/SortableList.ts +291 -67
- package/src/omnibox/Omnibox.ts +1 -1
- package/src/select/Select.ts +213 -42
- package/src/webchat/WebChat.ts +5 -2
- package/test/temba-flow-editor-node.test.ts +344 -0
- package/test/temba-flow-editor.test.ts +301 -0
- package/test/temba-flow-plumber.test.ts +189 -0
- package/test/temba-flow-render.test.ts +220 -0
- package/test/temba-omnibox.test.ts +2 -3
- package/test/temba-select.test.ts +180 -79
- package/test/temba-sortable-list.test.ts +108 -15
- package/test/temba-webchat-lightbox-fix.test.ts +57 -0
- package/test/utils.test.ts +52 -0
|
@@ -2,43 +2,119 @@ import { expect, fixture, oneEvent } from '@open-wc/testing';
|
|
|
2
2
|
import { html } from 'lit';
|
|
3
3
|
import { CustomEventType } from '../src/interfaces';
|
|
4
4
|
import { assertScreenshot, getClip } from './utils.test';
|
|
5
|
+
import { useFakeTimers } from 'sinon';
|
|
5
6
|
const BORING_LIST = html `
|
|
6
7
|
<temba-sortable-list>
|
|
7
|
-
<
|
|
8
|
-
|
|
8
|
+
<style>
|
|
9
|
+
.sortable {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
text-align: center;
|
|
14
|
+
height: 20px;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
<div class="sortable" id="chicken" style="">Chicken</div>
|
|
18
|
+
<div class="sortable" id="fish">Fish</div>
|
|
19
|
+
</temba-sortable-list>
|
|
20
|
+
`;
|
|
21
|
+
const HORIZONTAL_LIST = html `
|
|
22
|
+
<temba-sortable-list horizontal>
|
|
23
|
+
<style>
|
|
24
|
+
.sortable {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
text-align: center;
|
|
29
|
+
height: 20px;
|
|
30
|
+
width: 50px;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
33
|
+
<div class="sortable" id="red">Red</div>
|
|
34
|
+
<div class="sortable" id="blue">Blue</div>
|
|
35
|
+
<div class="sortable" id="green">Green</div>
|
|
9
36
|
</temba-sortable-list>
|
|
10
37
|
`;
|
|
11
38
|
const createSorter = async (def) => {
|
|
12
39
|
const parentNode = document.createElement('div');
|
|
13
|
-
parentNode.setAttribute('style', 'width:
|
|
40
|
+
parentNode.setAttribute('style', 'width: 100px;');
|
|
14
41
|
return (await fixture(def, { parentNode }));
|
|
15
42
|
};
|
|
16
43
|
describe('temba-sortable-list', () => {
|
|
44
|
+
let clock;
|
|
45
|
+
beforeEach(function () {
|
|
46
|
+
clock = useFakeTimers();
|
|
47
|
+
clock.runAll();
|
|
48
|
+
});
|
|
49
|
+
afterEach(function () {
|
|
50
|
+
clock.restore();
|
|
51
|
+
});
|
|
17
52
|
it('renders default', async () => {
|
|
18
53
|
const list = await createSorter(BORING_LIST);
|
|
19
54
|
await assertScreenshot('list/sortable', getClip(list));
|
|
20
55
|
});
|
|
21
|
-
it('
|
|
56
|
+
it('can get ids of sortable elements', async () => {
|
|
22
57
|
const list = await createSorter(BORING_LIST);
|
|
23
|
-
|
|
24
|
-
const
|
|
58
|
+
await list.updateComplete;
|
|
59
|
+
const ids = list.getIds();
|
|
60
|
+
expect(ids).to.deep.equal(['chicken', 'fish']);
|
|
61
|
+
});
|
|
62
|
+
it('works with horizontal layout', async () => {
|
|
63
|
+
const list = await createSorter(HORIZONTAL_LIST);
|
|
64
|
+
await list.updateComplete;
|
|
65
|
+
const ids = list.getIds();
|
|
66
|
+
expect(ids).to.deep.equal(['red', 'blue', 'green']);
|
|
67
|
+
// Test horizontal drag behavior
|
|
25
68
|
const bounds = list.getBoundingClientRect();
|
|
26
|
-
|
|
69
|
+
const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);
|
|
70
|
+
// Drag the first item (red) to after the second item (blue)
|
|
71
|
+
await moveMouse(bounds.left + 10, bounds.top + 10);
|
|
27
72
|
await mouseDown();
|
|
28
|
-
await moveMouse(bounds.left +
|
|
29
|
-
|
|
73
|
+
await moveMouse(bounds.left + 80, bounds.top + 10);
|
|
74
|
+
await mouseUp();
|
|
75
|
+
clock.runAll();
|
|
30
76
|
const orderEvent = await orderChanged;
|
|
31
77
|
expect(orderEvent.detail).to.deep.equal({
|
|
32
|
-
|
|
33
|
-
to: 'chicken',
|
|
34
|
-
fromIdx: 1,
|
|
35
|
-
toIdx: 0
|
|
78
|
+
swap: [0, 2]
|
|
36
79
|
});
|
|
80
|
+
});
|
|
81
|
+
it('handles prepareGhost callback', async () => {
|
|
82
|
+
const list = await createSorter(BORING_LIST);
|
|
83
|
+
let ghostPrepared = false;
|
|
84
|
+
list.prepareGhost = (ghost) => {
|
|
85
|
+
ghostPrepared = true;
|
|
86
|
+
ghost.style.backgroundColor = 'red';
|
|
87
|
+
};
|
|
88
|
+
const bounds = list.getBoundingClientRect();
|
|
89
|
+
// Start dragging to trigger ghost creation
|
|
90
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
91
|
+
await mouseDown();
|
|
92
|
+
await moveMouse(bounds.left + 30, bounds.bottom - 10);
|
|
93
|
+
expect(ghostPrepared).to.be.true;
|
|
94
|
+
// Clean up
|
|
95
|
+
await mouseUp();
|
|
96
|
+
clock.runAll();
|
|
97
|
+
});
|
|
98
|
+
it('drags', async () => {
|
|
99
|
+
const list = await createSorter(BORING_LIST);
|
|
100
|
+
const updated = oneEvent(list, 'change', false);
|
|
101
|
+
const bounds = list.getBoundingClientRect();
|
|
102
|
+
await moveMouse(bounds.left + 20, bounds.bottom - 10);
|
|
103
|
+
await mouseDown();
|
|
104
|
+
await moveMouse(bounds.left + 20, bounds.top + 5);
|
|
37
105
|
// should be hovered
|
|
38
106
|
await assertScreenshot('list/sortable-dragging', getClip(list));
|
|
39
|
-
// now lets drop
|
|
40
|
-
|
|
107
|
+
// now lets drop - this will fire the order changed event
|
|
108
|
+
const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);
|
|
41
109
|
await mouseUp();
|
|
110
|
+
clock.runAll();
|
|
111
|
+
await list.updateComplete;
|
|
112
|
+
clock.runAll();
|
|
113
|
+
// we should fire an order changed event on drop
|
|
114
|
+
const orderEvent = await orderChanged;
|
|
115
|
+
expect(orderEvent.detail).to.deep.equal({
|
|
116
|
+
swap: [1, 0]
|
|
117
|
+
});
|
|
42
118
|
await assertScreenshot('list/sortable-dropped', getClip(list));
|
|
43
119
|
// we should fire a change event
|
|
44
120
|
const changeEvent = await updated;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-sortable-list.test.js","sourceRoot":"","sources":["../../test/temba-sortable-list.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"temba-sortable-list.test.js","sourceRoot":"","sources":["../../test/temba-sortable-list.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, oneEvent } from '@open-wc/testing';\nimport { html, TemplateResult } from 'lit';\nimport { CustomEventType } from '../src/interfaces';\nimport { SortableList } from '../src/list/SortableList';\nimport { assertScreenshot, getClip } from './utils.test';\nimport Sinon, { useFakeTimers } from 'sinon';\n\nconst BORING_LIST = html`\n <temba-sortable-list>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n }\n </style>\n <div class=\"sortable\" id=\"chicken\" style=\"\">Chicken</div>\n <div class=\"sortable\" id=\"fish\">Fish</div>\n </temba-sortable-list>\n`;\n\nconst HORIZONTAL_LIST = html`\n <temba-sortable-list horizontal>\n <style>\n .sortable {\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n height: 20px;\n width: 50px;\n }\n </style>\n <div class=\"sortable\" id=\"red\">Red</div>\n <div class=\"sortable\" id=\"blue\">Blue</div>\n <div class=\"sortable\" id=\"green\">Green</div>\n </temba-sortable-list>\n`;\n\nconst createSorter = async (def: TemplateResult) => {\n const parentNode = document.createElement('div');\n parentNode.setAttribute('style', 'width: 100px;');\n return (await fixture(def, { parentNode })) as SortableList;\n};\n\ndescribe('temba-sortable-list', () => {\n let clock: Sinon.SinonFakeTimers;\n beforeEach(function () {\n clock = useFakeTimers();\n clock.runAll();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('renders default', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await assertScreenshot('list/sortable', getClip(list));\n });\n\n it('can get ids of sortable elements', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['chicken', 'fish']);\n });\n\n it('works with horizontal layout', async () => {\n const list: SortableList = await createSorter(HORIZONTAL_LIST);\n await list.updateComplete;\n\n const ids = list.getIds();\n expect(ids).to.deep.equal(['red', 'blue', 'green']);\n\n // Test horizontal drag behavior\n const bounds = list.getBoundingClientRect();\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n\n // Drag the first item (red) to after the second item (blue)\n await moveMouse(bounds.left + 10, bounds.top + 10);\n await mouseDown();\n await moveMouse(bounds.left + 80, bounds.top + 10);\n await mouseUp();\n clock.runAll();\n\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [0, 2]\n });\n });\n\n it('handles prepareGhost callback', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n let ghostPrepared = false;\n\n list.prepareGhost = (ghost: HTMLElement) => {\n ghostPrepared = true;\n ghost.style.backgroundColor = 'red';\n };\n\n const bounds = list.getBoundingClientRect();\n\n // Start dragging to trigger ghost creation\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 30, bounds.bottom - 10);\n\n expect(ghostPrepared).to.be.true;\n\n // Clean up\n await mouseUp();\n clock.runAll();\n });\n\n it('drags', async () => {\n const list: SortableList = await createSorter(BORING_LIST);\n const updated = oneEvent(list, 'change', false);\n\n const bounds = list.getBoundingClientRect();\n\n await moveMouse(bounds.left + 20, bounds.bottom - 10);\n await mouseDown();\n await moveMouse(bounds.left + 20, bounds.top + 5);\n\n // should be hovered\n await assertScreenshot('list/sortable-dragging', getClip(list));\n\n // now lets drop - this will fire the order changed event\n const orderChanged = oneEvent(list, CustomEventType.OrderChanged, false);\n await mouseUp();\n clock.runAll();\n await list.updateComplete;\n clock.runAll();\n\n // we should fire an order changed event on drop\n const orderEvent = await orderChanged;\n expect(orderEvent.detail).to.deep.equal({\n swap: [1, 0]\n });\n\n await assertScreenshot('list/sortable-dropped', getClip(list));\n\n // we should fire a change event\n const changeEvent = await updated;\n expect(changeEvent.type).to.equal('change');\n });\n});\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { getComponent } from './utils.test';
|
|
3
|
+
const getWebChat = async (attrs = {}) => {
|
|
4
|
+
const webChat = (await getComponent('temba-webchat', attrs, '', 400, 600));
|
|
5
|
+
return webChat;
|
|
6
|
+
};
|
|
7
|
+
describe('WebChat Lightbox Fix', () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
// Clean up any lightbox elements after each test
|
|
10
|
+
const lightboxes = document.querySelectorAll('temba-lightbox');
|
|
11
|
+
lightboxes.forEach((lightbox) => lightbox.remove());
|
|
12
|
+
});
|
|
13
|
+
it('should only create one lightbox element even with multiple WebChat instances', async () => {
|
|
14
|
+
// Create first WebChat instance
|
|
15
|
+
await getWebChat({
|
|
16
|
+
channel: 'test-channel-1'
|
|
17
|
+
});
|
|
18
|
+
// Verify lightbox was created
|
|
19
|
+
let lightboxes = document.querySelectorAll('temba-lightbox');
|
|
20
|
+
expect(lightboxes.length).to.equal(1);
|
|
21
|
+
// Create second WebChat instance
|
|
22
|
+
await getWebChat({
|
|
23
|
+
channel: 'test-channel-2'
|
|
24
|
+
});
|
|
25
|
+
// Verify still only one lightbox exists
|
|
26
|
+
lightboxes = document.querySelectorAll('temba-lightbox');
|
|
27
|
+
expect(lightboxes.length).to.equal(1);
|
|
28
|
+
});
|
|
29
|
+
it('should not create lightbox if one already exists', async () => {
|
|
30
|
+
// Manually create a lightbox first
|
|
31
|
+
const existingLightbox = document.createElement('temba-lightbox');
|
|
32
|
+
document.body.appendChild(existingLightbox);
|
|
33
|
+
// Create WebChat instance
|
|
34
|
+
await getWebChat({
|
|
35
|
+
channel: 'test-channel'
|
|
36
|
+
});
|
|
37
|
+
// Verify still only one lightbox exists
|
|
38
|
+
const lightboxes = document.querySelectorAll('temba-lightbox');
|
|
39
|
+
expect(lightboxes.length).to.equal(1);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=temba-webchat-lightbox-fix.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temba-webchat-lightbox-fix.test.js","sourceRoot":"","sources":["../../test/temba-webchat-lightbox-fix.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,UAAU,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE,EAAE;IAC3C,MAAM,OAAO,GAAG,CAAC,MAAM,YAAY,CACjC,eAAe,EACf,KAAK,EACL,EAAE,EACF,GAAG,EACH,GAAG,CACJ,CAAY,CAAC;IACd,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,SAAS,CAAC,GAAG,EAAE;QACb,iDAAiD;QACjD,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QAC/D,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,gCAAgC;QAChC,MAAM,UAAU,CAAC;YACf,OAAO,EAAE,gBAAgB;SAC1B,CAAC,CAAC;QAEH,8BAA8B;QAC9B,IAAI,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QAC7D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtC,iCAAiC;QACjC,MAAM,UAAU,CAAC;YACf,OAAO,EAAE,gBAAgB;SAC1B,CAAC,CAAC;QAEH,wCAAwC;QACxC,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QACzD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,mCAAmC;QACnC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAClE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;QAE5C,0BAA0B;QAC1B,MAAM,UAAU,CAAC;YACf,OAAO,EAAE,cAAc;SACxB,CAAC,CAAC;QAEH,wCAAwC;QACxC,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QAC/D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { WebChat } from '../src/webchat/WebChat';\nimport { getComponent } from './utils.test';\n\nconst getWebChat = async (attrs: any = {}) => {\n const webChat = (await getComponent(\n 'temba-webchat',\n attrs,\n '',\n 400,\n 600\n )) as WebChat;\n return webChat;\n};\n\ndescribe('WebChat Lightbox Fix', () => {\n afterEach(() => {\n // Clean up any lightbox elements after each test\n const lightboxes = document.querySelectorAll('temba-lightbox');\n lightboxes.forEach((lightbox) => lightbox.remove());\n });\n\n it('should only create one lightbox element even with multiple WebChat instances', async () => {\n // Create first WebChat instance\n await getWebChat({\n channel: 'test-channel-1'\n });\n\n // Verify lightbox was created\n let lightboxes = document.querySelectorAll('temba-lightbox');\n expect(lightboxes.length).to.equal(1);\n\n // Create second WebChat instance\n await getWebChat({\n channel: 'test-channel-2'\n });\n\n // Verify still only one lightbox exists\n lightboxes = document.querySelectorAll('temba-lightbox');\n expect(lightboxes.length).to.equal(1);\n });\n\n it('should not create lightbox if one already exists', async () => {\n // Manually create a lightbox first\n const existingLightbox = document.createElement('temba-lightbox');\n document.body.appendChild(existingLightbox);\n\n // Create WebChat instance\n await getWebChat({\n channel: 'test-channel'\n });\n\n // Verify still only one lightbox exists\n const lightboxes = document.querySelectorAll('temba-lightbox');\n expect(lightboxes.length).to.equal(1);\n });\n});\n"]}
|
|
@@ -200,4 +200,34 @@ export const mockNow = (isodate) => {
|
|
|
200
200
|
return now;
|
|
201
201
|
});
|
|
202
202
|
};
|
|
203
|
+
export const getOptions = (select) => {
|
|
204
|
+
return select.shadowRoot.querySelector('temba-options[visible]');
|
|
205
|
+
};
|
|
206
|
+
export const clickOption = async (clock, select, index) => {
|
|
207
|
+
const options = getOptions(select);
|
|
208
|
+
const option = options.shadowRoot.querySelector(`[data-option-index="${index}"]`);
|
|
209
|
+
await mouseClickElement(option);
|
|
210
|
+
await options.updateComplete;
|
|
211
|
+
await select.updateComplete;
|
|
212
|
+
await clock.runAll();
|
|
213
|
+
checkTimers(clock);
|
|
214
|
+
};
|
|
215
|
+
export const openSelect = async (clock, select) => {
|
|
216
|
+
const container = select.shadowRoot.querySelector('.select-container');
|
|
217
|
+
container.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
218
|
+
clock.runAll();
|
|
219
|
+
// add more explicit waiting and clock ticks
|
|
220
|
+
await select.updateComplete;
|
|
221
|
+
clock.runAll();
|
|
222
|
+
// ensure options are visible before proceeding
|
|
223
|
+
await waitFor(100);
|
|
224
|
+
clock.runAll();
|
|
225
|
+
};
|
|
226
|
+
export const openAndClick = async (clock, select, idx) => {
|
|
227
|
+
await openSelect(clock, select);
|
|
228
|
+
// Add this line to ensure proper timing when running as part of a test suite
|
|
229
|
+
await select.updateComplete;
|
|
230
|
+
clock.tick(50); // Give extra time for options to render
|
|
231
|
+
await clickOption(clock, select, idx);
|
|
232
|
+
};
|
|
203
233
|
//# sourceMappingURL=utils.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../test/utils.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAQjC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,WAAW,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAStC,MAAM,IAAI,GAAe,EAAE,CAAC;AAC5B,IAAI,KAAK,GAAe,EAAE,CAAC;AAC3B,IAAI,WAAW,CAAC;AAEhB,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;IAClC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAA,kBAAkB,CAAC,CAAC;IACpD,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAa,EAAE,EAAE,EAAE;IAC/C,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;SACzB,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE;QACpB,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACpC,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,GAAG,EACH,QAAa,EAAE,EACf,IAAI,GAAG,EAAE,EACT,KAAK,GAAG,GAAG,EACX,MAAM,GAAG,CAAC,EACV,KAAK,GAAG,EAAE,EACV,EAAE;IACF,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,GAAG,GAAG,CAAC;IAChE,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG;MACnB,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE;MACpC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE;MACvC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;GACrB,CAAC;IACF,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IACjD,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,EAAE;IAChC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE;QACpD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE;YACP,cAAc,EAAE,WAAW;YAC3B,GAAG,MAAM,CAAC,OAAO;SAClB;KACF,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,EAAE;IACpC,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;QACpE,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,MAAM,CAAC,OAAO;SAClB;KACF,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;IACpE,4CAA4C;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEpE,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtC,uCAAuC;YACvC,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,OAAO,cAAc,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,4BAA4B;IAC5B,OAAO,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,IAAI,EAAE;IAChB,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,GAAG,EAAE;IACR,MAAM,CAAC,KAAa,CAAC,OAAO,EAAE,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG;IAClB,oCAAoC,EAAE;QACpC,iDAAiD;KAClD;IACD,qCAAqC,EAAE;QACrC,kDAAkD;KACnD;IACD,oCAAoC,EAAE;QACpC,iDAAiD;KAClD;IACD,qCAAqC,EAAE;QACrC,kDAAkD;KACnD;IACD,4CAA4C,EAAE;QAC5C,uEAAuE;KACxE;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,EAAE;IAC1B,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,QAAgB,EAChB,IAAS,EACT,UAAe,EAAE,EACjB,MAAM,GAAG,KAAK,EACd,EAAE;IACF,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,QAAgB,EAChB,IAAS,EACT,UAAe,EAAE,EACjB,MAAM,GAAG,KAAK,EACd,EAAE;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE;IACjC,KAAK,GAAG,EAAE,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAU,EAAE,EAAE;IACxC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;IACnE,MAAM,CACJ,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAChC,0BAA0B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CACzD,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,EAAE;IACtC,OAAO,IAAI,OAAO,CAAC,UAAU,OAAO;QAClC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACnC,QAAgB,EAChB,IAAU,EACV,OAAoD,EACpD,EAAE;IACF,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,8EAA8E;IAC9E,MAAM,oBAAoB,GAAI,MAAc,CAAC,oBAAoB,CAAC;IAClE,MAAM,SAAS,GAAG,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACnD,MAAM,OAAO,GAAW,EAAE,CAAC;IAE3B,IAAI,CAAC;QACH,MAAO,MAAc,CAAC,iBAAiB,CACrC,GAAG,QAAQ,MAAM,EACjB,IAAI,EACJ,OAAO,EACP,SAAS,CACV,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,CAAC,OAAO,IACd,KAAK,CAAC,QAAQ;gBACZ,CAAC,CAAC,YAAY,KAAK,CAAC,QAAQ,YAAY,KAAK,CAAC,MAAM,EAAE;gBACtD,CAAC,CAAC,EACN,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACvD,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAgB,EAAE,EAAE;IAC1C,IAAI,IAAI,GAAQ,GAAG,CAAC,qBAAqB,EAAE,CAAC;IAC5C,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,OAAO,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC;IAE3B,MAAM,OAAO,GAAG;QACd,CAAC;QACD,CAAC;QACD,KAAK;QACL,MAAM;QACN,MAAM,EAAE,CAAC,GAAG,MAAM;QAClB,KAAK,EAAE,CAAC,GAAG,KAAK;QAChB,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EAAE,GAA0B,EAAE,EAAE;IACpE,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB,EAAE,CAAC;IAC3C,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAa,EAAE,EAAE,EAAE;IAC9C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;SACtB,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACjD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,QAAa,EAAE,EAAE,EAAE;IACtD,OAAO,IAAI,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;IAClC,MAAM,KAAK,GAAU,MAAM,OAAO,CAChC;;;;;;;OAOG,CACJ,CAAC;IACF,MAAM,KAAK,CAAC,mBAAmB,CAAC;IAChC,MAAM,KAAK,CAAC,mBAAmB,CAAC;IAEhC,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,wBAAwB;IACxB,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC,CAAC","sourcesContent":["import '../temba-modules';\nimport { DateTime } from 'luxon';\ninterface Clip {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nimport { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';\nimport MouseHelper from './MouseHelper';\nimport { Store } from '../src/store/Store';\nimport { replace, stub } from 'sinon';\n\nexport interface CodeMock {\n endpoint: RegExp;\n body: string;\n headers: any;\n status: string;\n}\n\nconst gets: CodeMock[] = [];\nlet posts: CodeMock[] = [];\nlet normalFetch;\n\nexport const showMouse = async () => {\n const mouse = await fixture(html`<mouse-helper />`);\n assert.instanceOf(mouse, MouseHelper);\n};\n\nexport const getAttributes = (attrs: any = {}) => {\n return `${Object.keys(attrs)\n .map((name: string) => {\n if (typeof attrs[name] === 'boolean' && attrs[name]) {\n return name;\n }\n return `${name}='${attrs[name]}'`;\n })\n .join(' ')}`;\n};\n\nexport const getComponent = async (\n tag,\n attrs: any = {},\n slot = '',\n width = 250,\n height = 0,\n style = ''\n) => {\n const spec = `<${tag} ${getAttributes(attrs)}>${slot}</${tag}>`;\n const parentNode = document.createElement('div');\n const styleAttribute = `\n ${width > 0 ? `width:${width}px;` : ``} \n ${height > 0 ? `height:${height}px;` : ``}\n ${style ? style : ``}\n `;\n parentNode.setAttribute('style', styleAttribute);\n return await fixture(spec, { parentNode });\n};\n\nconst createResponse = (mocked) => {\n const mockResponse = new window.Response(mocked.body, {\n status: mocked.status,\n headers: {\n 'Content-type': 'text/html',\n ...mocked.headers\n }\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst createJSONResponse = (mocked) => {\n const mockResponse = new window.Response(JSON.stringify(mocked.body), {\n status: mocked.status,\n headers: {\n 'Content-type': 'application/json',\n ...mocked.headers\n }\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst getResponse = (endpoint: string, options = { method: 'GET' }) => {\n // check if our path has been mocked in code\n const mocks = options.method === 'GET' ? gets : posts;\n const codeMock = mocks.find((mock) => mock.endpoint.test(endpoint));\n\n if (codeMock) {\n if (typeof codeMock.body === 'string') {\n // see if we are being mocked to a file\n if (codeMock.body.startsWith('/')) {\n endpoint = codeMock.body;\n } else {\n return createResponse(codeMock);\n }\n } else {\n return createJSONResponse(codeMock);\n }\n }\n // otherwise fetch over http\n return normalFetch(endpoint, options);\n};\n\nbefore(async () => {\n normalFetch = window.fetch;\n stub(window, 'fetch').callsFake(getResponse);\n await setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });\n});\n\nafter(() => {\n (window.fetch as any).restore();\n});\n\nconst mockMapping = {\n '/test-assets/api/users/admin1.json': [\n /\\/api\\/v2\\/users.json\\?email=admin1@nyaruka.com/\n ],\n '/test-assets/api/users/editor1.json': [\n /\\/api\\/v2\\/users.json\\?email=editor1@nyaruka.com/\n ],\n '/test-assets/api/users/agent1.json': [\n /\\/api\\/v2\\/users.json\\?email=agent1@nyaruka.com/\n ],\n '/test-assets/api/users/viewer1.json': [\n /\\/api\\/v2\\/users.json\\?email=viewer1@nyaruka.com/\n ],\n '/test-assets/contacts/contact-tickets.json': [\n /\\/api\\/v2\\/tickets.json\\?contact=24d64810-3315-4ff5-be85-48e3fe055bf9/\n ]\n};\n\nexport const mockAPI = () => {\n for (const key in mockMapping) {\n const urls = mockMapping[key];\n for (const url of urls) {\n mockGET(url, key);\n }\n }\n};\n\nexport const mockGET = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n gets.push({ endpoint, body, headers, status });\n};\n\nexport const mockPOST = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n posts.push({ endpoint, body, headers, status });\n};\n\nexport const clearMockPosts = () => {\n posts = [];\n};\n\nexport const checkTimers = (clock: any) => {\n expect(!!clock.timers).to.equal(true, 'Expected timers not found');\n expect(\n Object.keys(clock.timers).length,\n `Timers still to be run ${JSON.stringify(clock.timers)}`\n ).to.equal(0);\n};\n\nexport const delay = (millis: number) => {\n return new Promise(function (resolve) {\n window.setTimeout(resolve, millis);\n });\n};\n\nexport const assertScreenshot = async (\n filename: string,\n clip: Clip,\n waitFor?: { clock?: any; predicate?: () => boolean }\n) => {\n if (waitFor) {\n if (waitFor.clock) {\n waitFor.clock.restore();\n }\n await waitUntil(waitFor.predicate);\n }\n\n // detect if we're running in copilot's environment and use adaptive threshold\n const isCopilotEnvironment = (window as any).isCopilotEnvironment;\n const threshold = isCopilotEnvironment ? 1.0 : 0.1;\n const exclude: Clip[] = [];\n\n try {\n await (window as any).matchPageSnapshot(\n `${filename}.png`,\n clip,\n exclude,\n threshold\n );\n } catch (error) {\n if (error.message) {\n throw new Error(\n `${error.message} ${\n error.expected\n ? `Expected ${error.expected} but got ${error.actual}`\n : ''\n } ${error.files ? `\\n${error.files.join('\\n')}` : ''}`\n );\n }\n throw new Error(error);\n }\n};\n\nexport const getClip = (ele: HTMLElement) => {\n let clip: any = ele.getBoundingClientRect();\n if (!clip.width || !clip.height) {\n clip = ele.shadowRoot.firstElementChild.getBoundingClientRect();\n }\n\n const padding = 10;\n const width = clip.width + padding * 2;\n const height = clip.height + padding * 2;\n const y = clip.y - padding;\n const x = clip.x - padding;\n\n const newClip = {\n x,\n y,\n width,\n height,\n bottom: y + height,\n right: x + width,\n top: y,\n left: x\n };\n\n return newClip;\n};\n\nexport const mouseClickElement = async (ele: HTMLElement | Element) => {\n const bounds = ele.getBoundingClientRect();\n await mouseClick(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);\n};\n\nexport const getHTMLAttrs = (attrs: any = {}) => {\n return Object.keys(attrs)\n .map((name: string) => `${name}='${attrs[name]}'`)\n .join(' ');\n};\n\nexport const getHTML = (tag: string, attrs: any = {}) => {\n return `<${tag} ${getHTMLAttrs(attrs)}></${tag}>`;\n};\n\nexport const loadStore = async () => {\n const store: Store = await fixture(\n `<temba-store \n completion='/test-assets/store/editor.json'\n groups='/test-assets/store/groups.json'\n languages='/test-assets/store/languages.json'\n fields='/test-assets/store/fields.json'\n users='/test-assets/store/users.json'\n workspace='/test-assets/store/workspace.json'\n />`\n );\n await store.initialHttpComplete;\n await store.initialHttpComplete;\n\n return store;\n};\n\nexport const mockNow = (isodate: string) => {\n const now = DateTime.fromISO(isodate);\n // mock the current time\n replace(DateTime, 'now', () => {\n return now;\n });\n};\n"]}
|
|
1
|
+
{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../test/utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import '../temba-modules';\nimport { DateTime } from 'luxon';\ninterface Clip {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nimport { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';\nimport MouseHelper from './MouseHelper';\nimport { Store } from '../src/store/Store';\nimport { replace, stub } from 'sinon';\nimport { Select, SelectOption } from '../src/select/Select';\nimport { Options } from '../src/options/Options';\n\nexport interface CodeMock {\n endpoint: RegExp;\n body: string;\n headers: any;\n status: string;\n}\n\nconst gets: CodeMock[] = [];\nlet posts: CodeMock[] = [];\nlet normalFetch;\n\nexport const showMouse = async () => {\n const mouse = await fixture(html`<mouse-helper />`);\n assert.instanceOf(mouse, MouseHelper);\n};\n\nexport const getAttributes = (attrs: any = {}) => {\n return `${Object.keys(attrs)\n .map((name: string) => {\n if (typeof attrs[name] === 'boolean' && attrs[name]) {\n return name;\n }\n return `${name}='${attrs[name]}'`;\n })\n .join(' ')}`;\n};\n\nexport const getComponent = async (\n tag,\n attrs: any = {},\n slot = '',\n width = 250,\n height = 0,\n style = ''\n) => {\n const spec = `<${tag} ${getAttributes(attrs)}>${slot}</${tag}>`;\n const parentNode = document.createElement('div');\n const styleAttribute = `\n ${width > 0 ? `width:${width}px;` : ``} \n ${height > 0 ? `height:${height}px;` : ``}\n ${style ? style : ``}\n `;\n parentNode.setAttribute('style', styleAttribute);\n return await fixture(spec, { parentNode });\n};\n\nconst createResponse = (mocked) => {\n const mockResponse = new window.Response(mocked.body, {\n status: mocked.status,\n headers: {\n 'Content-type': 'text/html',\n ...mocked.headers\n }\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst createJSONResponse = (mocked) => {\n const mockResponse = new window.Response(JSON.stringify(mocked.body), {\n status: mocked.status,\n headers: {\n 'Content-type': 'application/json',\n ...mocked.headers\n }\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst getResponse = (endpoint: string, options = { method: 'GET' }) => {\n // check if our path has been mocked in code\n const mocks = options.method === 'GET' ? gets : posts;\n const codeMock = mocks.find((mock) => mock.endpoint.test(endpoint));\n\n if (codeMock) {\n if (typeof codeMock.body === 'string') {\n // see if we are being mocked to a file\n if (codeMock.body.startsWith('/')) {\n endpoint = codeMock.body;\n } else {\n return createResponse(codeMock);\n }\n } else {\n return createJSONResponse(codeMock);\n }\n }\n // otherwise fetch over http\n return normalFetch(endpoint, options);\n};\n\nbefore(async () => {\n normalFetch = window.fetch;\n stub(window, 'fetch').callsFake(getResponse);\n await setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });\n});\n\nafter(() => {\n (window.fetch as any).restore();\n});\n\nconst mockMapping = {\n '/test-assets/api/users/admin1.json': [\n /\\/api\\/v2\\/users.json\\?email=admin1@nyaruka.com/\n ],\n '/test-assets/api/users/editor1.json': [\n /\\/api\\/v2\\/users.json\\?email=editor1@nyaruka.com/\n ],\n '/test-assets/api/users/agent1.json': [\n /\\/api\\/v2\\/users.json\\?email=agent1@nyaruka.com/\n ],\n '/test-assets/api/users/viewer1.json': [\n /\\/api\\/v2\\/users.json\\?email=viewer1@nyaruka.com/\n ],\n '/test-assets/contacts/contact-tickets.json': [\n /\\/api\\/v2\\/tickets.json\\?contact=24d64810-3315-4ff5-be85-48e3fe055bf9/\n ]\n};\n\nexport const mockAPI = () => {\n for (const key in mockMapping) {\n const urls = mockMapping[key];\n for (const url of urls) {\n mockGET(url, key);\n }\n }\n};\n\nexport const mockGET = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n gets.push({ endpoint, body, headers, status });\n};\n\nexport const mockPOST = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n posts.push({ endpoint, body, headers, status });\n};\n\nexport const clearMockPosts = () => {\n posts = [];\n};\n\nexport const checkTimers = (clock: any) => {\n expect(!!clock.timers).to.equal(true, 'Expected timers not found');\n expect(\n Object.keys(clock.timers).length,\n `Timers still to be run ${JSON.stringify(clock.timers)}`\n ).to.equal(0);\n};\n\nexport const delay = (millis: number) => {\n return new Promise(function (resolve) {\n window.setTimeout(resolve, millis);\n });\n};\n\nexport const assertScreenshot = async (\n filename: string,\n clip: Clip,\n waitFor?: { clock?: any; predicate?: () => boolean }\n) => {\n if (waitFor) {\n if (waitFor.clock) {\n waitFor.clock.restore();\n }\n await waitUntil(waitFor.predicate);\n }\n\n // detect if we're running in copilot's environment and use adaptive threshold\n const isCopilotEnvironment = (window as any).isCopilotEnvironment;\n const threshold = isCopilotEnvironment ? 1.0 : 0.1;\n const exclude: Clip[] = [];\n\n try {\n await (window as any).matchPageSnapshot(\n `${filename}.png`,\n clip,\n exclude,\n threshold\n );\n } catch (error) {\n if (error.message) {\n throw new Error(\n `${error.message} ${\n error.expected\n ? `Expected ${error.expected} but got ${error.actual}`\n : ''\n } ${error.files ? `\\n${error.files.join('\\n')}` : ''}`\n );\n }\n throw new Error(error);\n }\n};\n\nexport const getClip = (ele: HTMLElement) => {\n let clip: any = ele.getBoundingClientRect();\n if (!clip.width || !clip.height) {\n clip = ele.shadowRoot.firstElementChild.getBoundingClientRect();\n }\n\n const padding = 10;\n const width = clip.width + padding * 2;\n const height = clip.height + padding * 2;\n const y = clip.y - padding;\n const x = clip.x - padding;\n\n const newClip = {\n x,\n y,\n width,\n height,\n bottom: y + height,\n right: x + width,\n top: y,\n left: x\n };\n\n return newClip;\n};\n\nexport const mouseClickElement = async (ele: HTMLElement | Element) => {\n const bounds = ele.getBoundingClientRect();\n await mouseClick(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);\n};\n\nexport const getHTMLAttrs = (attrs: any = {}) => {\n return Object.keys(attrs)\n .map((name: string) => `${name}='${attrs[name]}'`)\n .join(' ');\n};\n\nexport const getHTML = (tag: string, attrs: any = {}) => {\n return `<${tag} ${getHTMLAttrs(attrs)}></${tag}>`;\n};\n\nexport const loadStore = async () => {\n const store: Store = await fixture(\n `<temba-store \n completion='/test-assets/store/editor.json'\n groups='/test-assets/store/groups.json'\n languages='/test-assets/store/languages.json'\n fields='/test-assets/store/fields.json'\n users='/test-assets/store/users.json'\n workspace='/test-assets/store/workspace.json'\n />`\n );\n await store.initialHttpComplete;\n await store.initialHttpComplete;\n\n return store;\n};\n\nexport const mockNow = (isodate: string) => {\n const now = DateTime.fromISO(isodate);\n // mock the current time\n replace(DateTime, 'now', () => {\n return now;\n });\n};\n\nexport const getOptions = (select: Select<SelectOption>): Options => {\n return select.shadowRoot.querySelector('temba-options[visible]');\n};\n\nexport const clickOption = async (\n clock: any,\n select: Select<SelectOption>,\n index: number\n) => {\n const options = getOptions(select);\n const option = options.shadowRoot.querySelector(\n `[data-option-index=\"${index}\"]`\n ) as HTMLDivElement;\n\n await mouseClickElement(option);\n await options.updateComplete;\n await select.updateComplete;\n await clock.runAll();\n\n checkTimers(clock);\n};\nexport const openSelect = async (clock: any, select: Select<SelectOption>) => {\n const container = select.shadowRoot.querySelector('.select-container');\n container.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n\n clock.runAll();\n\n // add more explicit waiting and clock ticks\n await select.updateComplete;\n clock.runAll();\n\n // ensure options are visible before proceeding\n await waitFor(100);\n clock.runAll();\n};\n\nexport const openAndClick = async (\n clock: any,\n select: Select<SelectOption>,\n idx: number\n) => {\n await openSelect(clock, select);\n\n // Add this line to ensure proper timing when running as part of a test suite\n await select.updateComplete;\n clock.tick(50); // Give extra time for options to render\n\n await clickOption(clock, select, idx);\n};\n"]}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -121,7 +121,7 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
121
121
|
border-radius: var(--curvature);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
.featured temba-sortable-list .field:hover {
|
|
124
|
+
.featured:not(.dragging) temba-sortable-list .field:hover {
|
|
125
125
|
cursor: move;
|
|
126
126
|
border-color: #e6e6e6;
|
|
127
127
|
background: #fcfcfc;
|
|
@@ -138,9 +138,6 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
138
138
|
@property({ type: Object, attribute: false })
|
|
139
139
|
otherFieldKeys: string[] = [];
|
|
140
140
|
|
|
141
|
-
@property({ type: String })
|
|
142
|
-
draggingId: string;
|
|
143
|
-
|
|
144
141
|
@property({ type: String })
|
|
145
142
|
query = '';
|
|
146
143
|
|
|
@@ -189,36 +186,31 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
189
186
|
}
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
private handleSaveOrder(event) {
|
|
193
|
-
const list = event.currentTarget as SortableList;
|
|
194
|
-
postJSON(
|
|
195
|
-
this.priorityEndpoint,
|
|
196
|
-
list
|
|
197
|
-
.getIds()
|
|
198
|
-
.reverse()
|
|
199
|
-
.reduce((map, key, idx) => {
|
|
200
|
-
map[key] = idx;
|
|
201
|
-
return map;
|
|
202
|
-
}, {})
|
|
203
|
-
).then(() => {
|
|
204
|
-
this.store.refreshFields();
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
189
|
private handleOrderChanged(event) {
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
this.featuredFields[swapsies.fromIdx] = this.featuredFields[swapsies.toIdx];
|
|
212
|
-
this.featuredFields[swapsies.toIdx] = temp;
|
|
213
|
-
this.requestUpdate('featuredFields');
|
|
214
|
-
}
|
|
190
|
+
// Apply the reordering immediately - the SortableList now provides accurate indexes
|
|
191
|
+
const [fromIdx, toIdx] = event.detail.swap;
|
|
215
192
|
|
|
216
|
-
|
|
217
|
-
this.
|
|
218
|
-
|
|
193
|
+
const temp = this.featuredFields[fromIdx];
|
|
194
|
+
this.featuredFields.splice(fromIdx, 1);
|
|
195
|
+
this.featuredFields.splice(toIdx, 0, temp);
|
|
196
|
+
this.requestUpdate();
|
|
219
197
|
|
|
220
|
-
|
|
221
|
-
|
|
198
|
+
// Save the new order to the server
|
|
199
|
+
const list = event.currentTarget as SortableList;
|
|
200
|
+
setTimeout(() => {
|
|
201
|
+
postJSON(
|
|
202
|
+
this.priorityEndpoint,
|
|
203
|
+
list
|
|
204
|
+
.getIds()
|
|
205
|
+
.reverse()
|
|
206
|
+
.reduce((map, key, idx) => {
|
|
207
|
+
map[key] = idx;
|
|
208
|
+
return map;
|
|
209
|
+
}, {})
|
|
210
|
+
).then(() => {
|
|
211
|
+
this.store.refreshFields();
|
|
212
|
+
});
|
|
213
|
+
}, 0);
|
|
222
214
|
}
|
|
223
215
|
|
|
224
216
|
private handleFieldAction(event: MouseEvent) {
|
|
@@ -248,10 +240,13 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
248
240
|
display: flex;
|
|
249
241
|
flex-direction: row;
|
|
250
242
|
align-items: center;
|
|
251
|
-
padding: 0.25em 1em;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
243
|
+
padding: 0.25em 1em;"
|
|
244
|
+
@click=${(e: MouseEvent) => {
|
|
245
|
+
const ele = e.currentTarget as HTMLDivElement;
|
|
246
|
+
const key = ele.dataset.key;
|
|
247
|
+
const action = ele.dataset.action;
|
|
248
|
+
this.fireCustomEvent(CustomEventType.Selection, { key, action });
|
|
249
|
+
}}
|
|
255
250
|
>
|
|
256
251
|
<div
|
|
257
252
|
style="display: flex; min-width: 200px; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 2em"
|
|
@@ -327,10 +322,7 @@ export class FieldManager extends EndpointMonitorElement {
|
|
|
327
322
|
`
|
|
328
323
|
: html`
|
|
329
324
|
<temba-sortable-list
|
|
330
|
-
@change=${this.handleSaveOrder}
|
|
331
325
|
@temba-order-changed=${this.handleOrderChanged}
|
|
332
|
-
@temba-drag-start=${this.handleDragStart}
|
|
333
|
-
@temba-drag-stop=${this.handleDragStop}
|
|
334
326
|
>
|
|
335
327
|
${this.featuredFields.map((field) =>
|
|
336
328
|
this.renderField(field)
|