@nyaruka/temba-components 0.39.0 → 0.40.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 +17 -0
- package/dist/{9ac0723e.js → b885f7d6.js} +7 -34
- package/dist/index.js +7 -34
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/dist/templates/components-body.html +1 -1
- package/dist/templates/components-head.html +1 -1
- package/out-tsc/src/contacts/ContactBadges.js +5 -5
- package/out-tsc/src/contacts/ContactBadges.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +77 -168
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/utils/index.js +2 -0
- package/out-tsc/src/utils/index.js.map +1 -1
- package/out-tsc/src/vectoricon/index.js +2 -0
- package/out-tsc/src/vectoricon/index.js.map +1 -1
- package/out-tsc/test/temba-label.test.js +4 -4
- package/out-tsc/test/temba-label.test.js.map +1 -1
- package/out-tsc/test/temba-menu.test.js +58 -7
- package/out-tsc/test/temba-menu.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/label/custom.png +0 -0
- package/screenshots/truth/label/danger.png +0 -0
- package/screenshots/truth/label/default-icon.png +0 -0
- package/screenshots/truth/label/shadow.png +0 -0
- package/screenshots/truth/menu/menu-focus.png +0 -0
- package/screenshots/truth/menu/menu-focused-with items.png +0 -0
- package/screenshots/truth/menu/menu-refresh-1.png +0 -0
- package/screenshots/truth/menu/menu-refresh-2.png +0 -0
- package/screenshots/truth/menu/menu-root.png +0 -0
- package/screenshots/truth/menu/menu-submenu.png +0 -0
- package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
- package/screenshots/truth/menu/menu-tasks.png +0 -0
- package/src/contacts/ContactBadges.ts +5 -5
- package/src/list/TembaMenu.ts +89 -181
- package/src/utils/index.ts +3 -0
- package/src/vectoricon/index.ts +2 -0
- package/test/temba-label.test.ts +4 -4
- package/test/temba-menu.test.ts +73 -7
- package/test-assets/menu/menu-root.json +33 -0
- package/test-assets/menu/menu-schedule.json +21 -0
- package/test-assets/{list → menu}/menu-tasks.json +0 -0
- package/screenshots/truth/list/menu-root.png +0 -0
- package/screenshots/truth/list/menu-submenu.png +0 -0
- package/test-assets/list/menu-root.json +0 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temba-menu.test.js","sourceRoot":"","sources":["../../test/temba-menu.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,GAAG,GAAG,YAAY,CAAC;AACzB,MAAM,OAAO,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,YAAY,CAC9B,GAAG,EACH,KAAK,EACL,EAAE,EACF,KAAK,EACL,CAAC,EACD,sBAAsB,CACvB,CAAc,CAAC;IAEhB,qBAAqB;IACrB,MAAM,IAAI,CAAC,YAAY,CAAC;IAExB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,IAAI,GAAc,MAAM,OAAO,EAAE,CAAC;QACxC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,IAAI,GAAc,MAAM,OAAO,CAAC;YACpC,QAAQ,EAAE,kCAAkC;SAC7C,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,gBAAgB,CAAC,gBAAgB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAChC,MAAM,IAAI,GAAc,MAAM,OAAO,CAAC;YACpC,QAAQ,EAAE,kCAAkC;SAC7C,CAAC,CAAC;QAEH,
|
|
1
|
+
{"version":3,"file":"temba-menu.test.js","sourceRoot":"","sources":["../../test/temba-menu.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, expect } from '@open-wc/testing';\n\nimport { TembaMenu } from '../src/list/TembaMenu';\nimport { assertScreenshot, getClip, getComponent } from './utils.test';\n\nconst TAG = 'temba-menu';\nconst getMenu = async (attrs: any = {}, width = 0) => {\n const menu = (await getComponent(\n TAG,\n attrs,\n '',\n width,\n 0,\n 'display:inline-block'\n )) as TembaMenu;\n\n // wait for the fetch\n await menu.httpComplete;\n\n return menu;\n};\n\nconst IDX_CHOOSER = 0;\nconst IDX_TASKS = 1;\nconst IDX_SCHEDULE = 2;\n\ndescribe('temba-menu', () => {\n it('can be created', async () => {\n const list: TembaMenu = await getMenu();\n assert.instanceOf(list, TembaMenu);\n expect(list.root).is.undefined;\n });\n\n it('renders with endpoint', async () => {\n const menu: TembaMenu = await getMenu({\n endpoint: '/test-assets/menu/menu-root.json',\n });\n\n expect(menu.root.items.length).to.equal(3);\n await assertScreenshot('menu/menu-root', getClip(menu));\n });\n\n it('supports submenu', async () => {\n const menu: TembaMenu = await getMenu({\n endpoint: '/test-assets/menu/menu-root.json',\n });\n\n // click our tasks\n menu.getDiv('#menu-tasks').click();\n await menu.httpComplete;\n menu.requestUpdate();\n // await menu.updateComplete;\n\n expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);\n await assertScreenshot('menu/menu-submenu', getClip(menu));\n });\n\n it('sets focus', async () => {\n // setting focus just shows the selection, it does\n // not trigger events such as loading or dispatching\n\n const menu: TembaMenu = await getMenu({\n endpoint: '/test-assets/menu/menu-root.json',\n });\n\n // click our tasks\n menu.getDiv('#menu-tasks').click();\n await menu.httpComplete;\n\n // now set the focus manually\n menu.setFocusedItem('schedule');\n\n // setting focus does NOT fetch items\n expect(menu.root.items[IDX_SCHEDULE].items).to.equal(undefined);\n\n // now load the items explicitly\n menu.getDiv('#menu-schedule').click();\n await menu.httpComplete;\n expect(menu.root.items[IDX_SCHEDULE].items.length).to.equal(3);\n\n await assertScreenshot('menu/menu-focused-with items', getClip(menu));\n\n menu.setFocusedItem('tasks');\n await assertScreenshot('menu/menu-tasks', getClip(menu));\n\n menu.setFocusedItem('tasks/todo');\n await assertScreenshot('menu/menu-tasks-nextup', getClip(menu));\n });\n\n it('refreshes', async () => {\n // the menu should refresh along the selection path without destroying state\n const menu: TembaMenu = await getMenu({\n endpoint: '/test-assets/menu/menu-root.json',\n });\n\n // click our tasks\n menu.getDiv('#menu-tasks').click();\n menu.requestUpdate();\n await menu.httpComplete;\n // await menu.updateComplete;\n\n // now click on the todo\n menu.getDiv('#menu-todo').click();\n menu.requestUpdate();\n // await menu.updateComplete;\n\n expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);\n await assertScreenshot('menu/menu-refresh-1', getClip(menu));\n\n // now refresh!\n menu.refresh();\n await menu.httpComplete;\n\n // we should still have our task items\n expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);\n await assertScreenshot('menu/menu-refresh-2', getClip(menu));\n });\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
|
|
@@ -4,9 +4,9 @@ import { Icon } from '../vectoricon';
|
|
|
4
4
|
import { ContactStoreElement } from './ContactStoreElement';
|
|
5
5
|
|
|
6
6
|
const STATUS = {
|
|
7
|
-
stopped: { name: 'Stopped'
|
|
8
|
-
blocked: { name: 'Blocked'
|
|
9
|
-
archived: { name: 'Archived'
|
|
7
|
+
stopped: { name: 'Stopped' },
|
|
8
|
+
blocked: { name: 'Blocked' },
|
|
9
|
+
archived: { name: 'Archived' },
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export class ContactBadges extends ContactStoreElement {
|
|
@@ -32,9 +32,9 @@ export class ContactBadges extends ContactStoreElement {
|
|
|
32
32
|
${status && this.data.status !== 'active'
|
|
33
33
|
? html`
|
|
34
34
|
<temba-label
|
|
35
|
-
icon="${status
|
|
35
|
+
icon="icon.contact_${this.data.status}"
|
|
36
36
|
onclick="goto(event)"
|
|
37
|
-
href="/contact/${status.name.toLowerCase()}"
|
|
37
|
+
href="/contact/${status.name.toLowerCase()}/"
|
|
38
38
|
secondary
|
|
39
39
|
clickable
|
|
40
40
|
shadow
|
package/src/list/TembaMenu.ts
CHANGED
|
@@ -18,7 +18,6 @@ export interface MenuItem {
|
|
|
18
18
|
loading?: boolean;
|
|
19
19
|
bottom?: boolean;
|
|
20
20
|
level?: number;
|
|
21
|
-
trigger?: string;
|
|
22
21
|
href?: string;
|
|
23
22
|
show_header?: boolean;
|
|
24
23
|
items?: MenuItem[];
|
|
@@ -122,9 +121,6 @@ export class TembaMenu extends RapidElement {
|
|
|
122
121
|
display: none;
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
.submenu {
|
|
126
|
-
}
|
|
127
|
-
|
|
128
124
|
.level-0 > .item,
|
|
129
125
|
.level-0 > temba-dropdown > div[slot='toggle'] > .avatar {
|
|
130
126
|
background: var(--color-primary-dark);
|
|
@@ -185,9 +181,6 @@ export class TembaMenu extends RapidElement {
|
|
|
185
181
|
font-size: 0.7em;
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
.level-0.expanding {
|
|
189
|
-
}
|
|
190
|
-
|
|
191
184
|
.level-0.expanded {
|
|
192
185
|
background: inherit;
|
|
193
186
|
}
|
|
@@ -218,9 +211,6 @@ export class TembaMenu extends RapidElement {
|
|
|
218
211
|
margin-right: 0.5em;
|
|
219
212
|
}
|
|
220
213
|
|
|
221
|
-
.item.inline > temba-icon {
|
|
222
|
-
}
|
|
223
|
-
|
|
224
214
|
.item > .details > .name {
|
|
225
215
|
flex-grow: 1;
|
|
226
216
|
white-space: nowrap;
|
|
@@ -270,9 +260,6 @@ export class TembaMenu extends RapidElement {
|
|
|
270
260
|
border-bottom-right-radius: var(--curvature);
|
|
271
261
|
}
|
|
272
262
|
|
|
273
|
-
.level-0 > .selected-top {
|
|
274
|
-
}
|
|
275
|
-
|
|
276
263
|
.level-0 > .item:hover {
|
|
277
264
|
background: rgba(var(--primary-rgb), 0.85);
|
|
278
265
|
--icon-color: #fff;
|
|
@@ -284,12 +271,6 @@ export class TembaMenu extends RapidElement {
|
|
|
284
271
|
cursor: default;
|
|
285
272
|
}
|
|
286
273
|
|
|
287
|
-
.inline-children {
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.inline-children .item {
|
|
291
|
-
}
|
|
292
|
-
|
|
293
274
|
.item.inline {
|
|
294
275
|
border: 0px solid transparent;
|
|
295
276
|
}
|
|
@@ -330,9 +311,6 @@ export class TembaMenu extends RapidElement {
|
|
|
330
311
|
transition: min-width var(--transition-speed) !important;
|
|
331
312
|
}
|
|
332
313
|
|
|
333
|
-
.level-1 .item .details {
|
|
334
|
-
}
|
|
335
|
-
|
|
336
314
|
.collapsed .item {
|
|
337
315
|
overflow: hidden;
|
|
338
316
|
min-width: 0;
|
|
@@ -346,12 +324,6 @@ export class TembaMenu extends RapidElement {
|
|
|
346
324
|
align-items: center;
|
|
347
325
|
}
|
|
348
326
|
|
|
349
|
-
.item .details .name {
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.item temba-icon {
|
|
353
|
-
}
|
|
354
|
-
|
|
355
327
|
.collapsed .item {
|
|
356
328
|
margin-bottom: 0.5em;
|
|
357
329
|
}
|
|
@@ -536,7 +508,6 @@ export class TembaMenu extends RapidElement {
|
|
|
536
508
|
|
|
537
509
|
root: MenuItem;
|
|
538
510
|
selection: string[] = [];
|
|
539
|
-
pending: string[] = [];
|
|
540
511
|
state: { [id: string]: MenuItemState } = {};
|
|
541
512
|
|
|
542
513
|
constructor() {
|
|
@@ -566,14 +537,6 @@ export class TembaMenu extends RapidElement {
|
|
|
566
537
|
}
|
|
567
538
|
|
|
568
539
|
public updated(changes: Map<string, any>) {
|
|
569
|
-
if (changes.has('value')) {
|
|
570
|
-
this.setSelection((this.value || '').split('/'));
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (changes.has('submenu') && !changes.has('value')) {
|
|
574
|
-
this.setSelection([this.submenu]);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
540
|
if (changes.has('endpoint')) {
|
|
578
541
|
this.root = {
|
|
579
542
|
level: -1,
|
|
@@ -582,9 +545,16 @@ export class TembaMenu extends RapidElement {
|
|
|
582
545
|
|
|
583
546
|
if (!this.wait) {
|
|
584
547
|
this.loadItems(this.root);
|
|
548
|
+
} else {
|
|
549
|
+
this.fireCustomEvent(CustomEventType.Ready);
|
|
585
550
|
}
|
|
551
|
+
}
|
|
586
552
|
|
|
587
|
-
|
|
553
|
+
if (changes.has('root')) {
|
|
554
|
+
if (this.value) {
|
|
555
|
+
this.setFocusedItem(this.value);
|
|
556
|
+
this.value = null;
|
|
557
|
+
}
|
|
588
558
|
}
|
|
589
559
|
}
|
|
590
560
|
|
|
@@ -592,53 +562,36 @@ export class TembaMenu extends RapidElement {
|
|
|
592
562
|
this.loadItems(this.root);
|
|
593
563
|
}
|
|
594
564
|
|
|
595
|
-
public refresh(
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// go up the tree until we find an endpoint
|
|
601
|
-
const item = this.getMenuItemForSelection(path);
|
|
565
|
+
public refresh() {
|
|
566
|
+
const path = [...this.selection];
|
|
567
|
+
let item = this.root;
|
|
602
568
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
path.pop();
|
|
608
|
-
this.refresh(path);
|
|
609
|
-
}
|
|
569
|
+
while (path.length > 0) {
|
|
570
|
+
this.loadItems(item);
|
|
571
|
+
const id = path.shift();
|
|
572
|
+
item = item.items.find(_item => _item.id == id);
|
|
610
573
|
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
private fireNoPath(missingId: string) {
|
|
614
|
-
const item = this.getMenuItem();
|
|
615
|
-
|
|
616
|
-
if (item) {
|
|
617
|
-
const details = {
|
|
618
|
-
item: item.id,
|
|
619
|
-
selection: '/' + this.selection.join('/'),
|
|
620
|
-
endpoint: item.endpoint,
|
|
621
|
-
path:
|
|
622
|
-
missingId + '/' + this.pending.join('/') + document.location.search,
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
// remove any excess from our selection
|
|
626
|
-
const selection = this.selection.join('/');
|
|
627
|
-
selection.replace(details.path, '');
|
|
628
|
-
this.selection = selection.split('/');
|
|
629
574
|
|
|
630
|
-
|
|
631
|
-
this.pending = [];
|
|
632
|
-
this.requestUpdate('root');
|
|
633
|
-
}
|
|
575
|
+
this.loadItems(item);
|
|
634
576
|
}
|
|
635
577
|
|
|
636
578
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
637
|
-
private loadItems(item: MenuItem, selectFirst =
|
|
579
|
+
private loadItems(item: MenuItem, selectFirst = false) {
|
|
638
580
|
if (item && item.endpoint) {
|
|
639
581
|
item.loading = true;
|
|
640
582
|
this.httpComplete = fetchResults(item.endpoint).then(
|
|
641
583
|
(items: MenuItem[]) => {
|
|
584
|
+
items.forEach(newItem => {
|
|
585
|
+
if (!newItem.items) {
|
|
586
|
+
const prevItem = (item.items || []).find(
|
|
587
|
+
prev => prev.id == newItem.id
|
|
588
|
+
);
|
|
589
|
+
if (prevItem && prevItem.items) {
|
|
590
|
+
newItem.items = prevItem.items;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
642
595
|
// update our item level
|
|
643
596
|
items.forEach(subItem => {
|
|
644
597
|
subItem.level = item.level + 1;
|
|
@@ -646,42 +599,30 @@ export class TembaMenu extends RapidElement {
|
|
|
646
599
|
if (subItem.items) {
|
|
647
600
|
subItem.items.forEach(inlineItem => {
|
|
648
601
|
inlineItem.level = item.level + 2;
|
|
649
|
-
// inlineItem.parent = subItem;
|
|
650
602
|
});
|
|
651
603
|
}
|
|
652
604
|
});
|
|
653
605
|
|
|
654
606
|
item.items = items;
|
|
655
607
|
item.loading = false;
|
|
608
|
+
|
|
609
|
+
if (this.submenu && this.selection.length == 0) {
|
|
610
|
+
const sub = this.getMenuItemForSelection([this.submenu]);
|
|
611
|
+
this.handleItemClicked(null, sub);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (!this.wait) {
|
|
615
|
+
this.fireCustomEvent(CustomEventType.Ready);
|
|
616
|
+
this.wait = true;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// once we've set our items check if we need to auto-select
|
|
620
|
+
if (selectFirst && item.items.length > 0) {
|
|
621
|
+
this.handleItemClicked(null, item.items[0]);
|
|
622
|
+
}
|
|
623
|
+
|
|
656
624
|
this.requestUpdate('root');
|
|
657
625
|
this.scrollSelectedIntoView();
|
|
658
|
-
if (this.pending && this.pending.length > 0) {
|
|
659
|
-
// auto select the next pending click
|
|
660
|
-
const nextId = this.pending.shift();
|
|
661
|
-
if (nextId && items.length > 0) {
|
|
662
|
-
const nextItem = findItem(items, nextId);
|
|
663
|
-
if (nextItem.item) {
|
|
664
|
-
this.handleItemClicked(null, nextItem.item);
|
|
665
|
-
} else {
|
|
666
|
-
this.fireNoPath(nextId);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
} else {
|
|
670
|
-
// auto select the first item
|
|
671
|
-
if (
|
|
672
|
-
selectFirst &&
|
|
673
|
-
items.length > 0 &&
|
|
674
|
-
this.selection.length >= 1 &&
|
|
675
|
-
!item.inline
|
|
676
|
-
) {
|
|
677
|
-
for (const item of items) {
|
|
678
|
-
if (!item.type) {
|
|
679
|
-
this.handleItemClicked(null, item);
|
|
680
|
-
break;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
626
|
}
|
|
686
627
|
);
|
|
687
628
|
}
|
|
@@ -692,15 +633,27 @@ export class TembaMenu extends RapidElement {
|
|
|
692
633
|
menuItem: MenuItem,
|
|
693
634
|
parent: MenuItem = null
|
|
694
635
|
) {
|
|
695
|
-
this.fireCustomEvent(CustomEventType.ButtonClicked, {
|
|
696
|
-
item: menuItem,
|
|
697
|
-
parent,
|
|
698
|
-
});
|
|
699
636
|
if (parent && parent.popup) {
|
|
637
|
+
if (event) {
|
|
638
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, {
|
|
639
|
+
item: menuItem,
|
|
640
|
+
selection: this.getSelection(),
|
|
641
|
+
parent,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
700
645
|
return;
|
|
701
646
|
}
|
|
702
647
|
|
|
703
648
|
if (menuItem.popup) {
|
|
649
|
+
if (event) {
|
|
650
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, {
|
|
651
|
+
item: menuItem,
|
|
652
|
+
selection: this.getSelection(),
|
|
653
|
+
parent,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
704
657
|
return;
|
|
705
658
|
}
|
|
706
659
|
|
|
@@ -717,63 +670,37 @@ export class TembaMenu extends RapidElement {
|
|
|
717
670
|
event.stopPropagation();
|
|
718
671
|
}
|
|
719
672
|
|
|
720
|
-
|
|
721
|
-
|
|
673
|
+
// update our selection
|
|
674
|
+
if (menuItem.level >= this.selection.length) {
|
|
675
|
+
this.selection.push(menuItem.vanity_id || menuItem.id);
|
|
722
676
|
} else {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// update our selection
|
|
732
|
-
if (menuItem.level >= this.selection.length) {
|
|
733
|
-
this.selection.push(menuItem.vanity_id || menuItem.id);
|
|
734
|
-
} else {
|
|
735
|
-
this.selection.splice(
|
|
736
|
-
menuItem.level,
|
|
737
|
-
this.selection.length - menuItem.level,
|
|
738
|
-
menuItem.vanity_id || menuItem.id
|
|
739
|
-
);
|
|
740
|
-
}
|
|
677
|
+
this.selection.splice(
|
|
678
|
+
menuItem.level,
|
|
679
|
+
this.selection.length - menuItem.level,
|
|
680
|
+
menuItem.vanity_id || menuItem.id
|
|
681
|
+
);
|
|
682
|
+
}
|
|
741
683
|
|
|
742
|
-
|
|
743
|
-
|
|
684
|
+
if (menuItem.endpoint) {
|
|
685
|
+
this.loadItems(menuItem, !!event);
|
|
744
686
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
if (this.pending && this.pending.length > 0) {
|
|
751
|
-
// auto select the next pending click
|
|
752
|
-
const nextId = this.pending.shift();
|
|
753
|
-
const item = this.getMenuItem();
|
|
754
|
-
if (nextId && item && item.items && item.items.length > 0) {
|
|
755
|
-
const nextItem = findItem(item.items, nextId).item;
|
|
756
|
-
if (nextItem) {
|
|
757
|
-
this.handleItemClicked(null, nextItem);
|
|
758
|
-
return;
|
|
759
|
-
} else {
|
|
760
|
-
this.fireNoPath(nextId);
|
|
761
|
-
this.requestUpdate('root');
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
} else {
|
|
765
|
-
this.fireNoPath(nextId);
|
|
766
|
-
this.requestUpdate('root');
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
this.requestUpdate('root');
|
|
687
|
+
// make sure change events fire for events with hrefs
|
|
688
|
+
if (!menuItem.href) {
|
|
689
|
+
return;
|
|
771
690
|
}
|
|
691
|
+
} else {
|
|
692
|
+
this.requestUpdate();
|
|
693
|
+
}
|
|
772
694
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
695
|
+
if (menuItem.href) {
|
|
696
|
+
this.dispatchEvent(new Event('change'));
|
|
776
697
|
}
|
|
698
|
+
|
|
699
|
+
this.fireCustomEvent(CustomEventType.ButtonClicked, {
|
|
700
|
+
item: menuItem,
|
|
701
|
+
selection: this.getSelection(),
|
|
702
|
+
parent,
|
|
703
|
+
});
|
|
777
704
|
}
|
|
778
705
|
|
|
779
706
|
public scrollSelectedIntoView() {
|
|
@@ -828,28 +755,6 @@ export class TembaMenu extends RapidElement {
|
|
|
828
755
|
return this.selection;
|
|
829
756
|
}
|
|
830
757
|
|
|
831
|
-
public setSelection(path: string[]) {
|
|
832
|
-
this.pending = [...path];
|
|
833
|
-
this.selection = [];
|
|
834
|
-
|
|
835
|
-
if (this.wait) {
|
|
836
|
-
this.wait = false;
|
|
837
|
-
this.loadItems(this.root);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
public setSelectionPath(path: string) {
|
|
842
|
-
const asPath = path.split('/').filter(step => !!step);
|
|
843
|
-
|
|
844
|
-
// first try to click in the current space
|
|
845
|
-
const clicked = this.clickItem(asPath[asPath.length - 1]);
|
|
846
|
-
|
|
847
|
-
if (!clicked) {
|
|
848
|
-
this.wait = true;
|
|
849
|
-
this.setSelection(asPath);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
758
|
public handleExpand() {
|
|
854
759
|
this.collapsed = false;
|
|
855
760
|
}
|
|
@@ -860,6 +765,9 @@ export class TembaMenu extends RapidElement {
|
|
|
860
765
|
|
|
861
766
|
public async setFocusedItem(path: string) {
|
|
862
767
|
const focusedPath = path.split('/').filter(step => !!step);
|
|
768
|
+
if (!this.root) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
863
771
|
|
|
864
772
|
// if we don't match at the first level, we are a noop
|
|
865
773
|
if (focusedPath.length > 0) {
|
|
@@ -875,7 +783,7 @@ export class TembaMenu extends RapidElement {
|
|
|
875
783
|
const nextId = focusedPath.shift();
|
|
876
784
|
if (nextId) {
|
|
877
785
|
if (!level.items) {
|
|
878
|
-
this.loadItems(level
|
|
786
|
+
this.loadItems(level);
|
|
879
787
|
await this.httpComplete;
|
|
880
788
|
}
|
|
881
789
|
|
package/src/utils/index.ts
CHANGED
|
@@ -179,6 +179,7 @@ export interface WebResponse {
|
|
|
179
179
|
url?: string;
|
|
180
180
|
headers: Headers;
|
|
181
181
|
controller?: AbortController;
|
|
182
|
+
redirected?: boolean;
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
export const postUrl = (
|
|
@@ -213,6 +214,8 @@ export const postUrl = (
|
|
|
213
214
|
json,
|
|
214
215
|
headers: response.headers,
|
|
215
216
|
status: response.status,
|
|
217
|
+
redirected: response.redirected,
|
|
218
|
+
url: response.url,
|
|
216
219
|
});
|
|
217
220
|
});
|
|
218
221
|
})
|
package/src/vectoricon/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export enum Icon {
|
|
|
21
21
|
checkbox = 'square',
|
|
22
22
|
checkbox_checked = 'check-square',
|
|
23
23
|
contact = 'user-01',
|
|
24
|
+
contact_archived = 'archive',
|
|
24
25
|
contact_blocked = 'message-x-square',
|
|
25
26
|
contact_stopped = 'slash-octagon',
|
|
26
27
|
contact_updated = 'user-edit',
|
|
@@ -43,6 +44,7 @@ export enum Icon {
|
|
|
43
44
|
group = 'users-01',
|
|
44
45
|
group_smart = 'atom-01',
|
|
45
46
|
help = 'help-circle',
|
|
47
|
+
home = 'home-smile',
|
|
46
48
|
inbox = 'inbox-01',
|
|
47
49
|
label = 'tag-01',
|
|
48
50
|
language = 'globe-01',
|
package/test/temba-label.test.ts
CHANGED
|
@@ -15,13 +15,13 @@ describe('temba-label', () => {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
it('renders icon', async () => {
|
|
18
|
-
const label: Label = await getLabel('Default', { icon: '
|
|
18
|
+
const label: Label = await getLabel('Default', { icon: 'check' });
|
|
19
19
|
await assertScreenshot('label/default-icon', getClip(label));
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it('renders shadow', async () => {
|
|
23
23
|
const label: Label = await getLabel('Shadow', {
|
|
24
|
-
icon: '
|
|
24
|
+
icon: 'check',
|
|
25
25
|
shadow: true,
|
|
26
26
|
});
|
|
27
27
|
await assertScreenshot('label/shadow', getClip(label));
|
|
@@ -58,7 +58,7 @@ describe('temba-label', () => {
|
|
|
58
58
|
|
|
59
59
|
it('renders danger', async () => {
|
|
60
60
|
const label: Label = await getLabel('Danger', {
|
|
61
|
-
icon: '
|
|
61
|
+
icon: 'check',
|
|
62
62
|
danger: true,
|
|
63
63
|
});
|
|
64
64
|
await assertScreenshot('label/danger', getClip(label));
|
|
@@ -66,7 +66,7 @@ describe('temba-label', () => {
|
|
|
66
66
|
|
|
67
67
|
it('renders custom', async () => {
|
|
68
68
|
const label: Label = await getLabel('Custom Orange', {
|
|
69
|
-
icon: '
|
|
69
|
+
icon: 'check',
|
|
70
70
|
backgroundColor: 'rgb(240, 176, 29)',
|
|
71
71
|
textColor: '#ffff',
|
|
72
72
|
});
|
package/test/temba-menu.test.ts
CHANGED
|
@@ -20,6 +20,10 @@ const getMenu = async (attrs: any = {}, width = 0) => {
|
|
|
20
20
|
return menu;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const IDX_CHOOSER = 0;
|
|
24
|
+
const IDX_TASKS = 1;
|
|
25
|
+
const IDX_SCHEDULE = 2;
|
|
26
|
+
|
|
23
27
|
describe('temba-menu', () => {
|
|
24
28
|
it('can be created', async () => {
|
|
25
29
|
const list: TembaMenu = await getMenu();
|
|
@@ -29,24 +33,86 @@ describe('temba-menu', () => {
|
|
|
29
33
|
|
|
30
34
|
it('renders with endpoint', async () => {
|
|
31
35
|
const menu: TembaMenu = await getMenu({
|
|
32
|
-
endpoint: '/test-assets/
|
|
36
|
+
endpoint: '/test-assets/menu/menu-root.json',
|
|
33
37
|
});
|
|
34
38
|
|
|
35
|
-
expect(menu.root.items.length).to.equal(
|
|
36
|
-
await assertScreenshot('
|
|
39
|
+
expect(menu.root.items.length).to.equal(3);
|
|
40
|
+
await assertScreenshot('menu/menu-root', getClip(menu));
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
it('supports submenu', async () => {
|
|
40
44
|
const menu: TembaMenu = await getMenu({
|
|
41
|
-
endpoint: '/test-assets/
|
|
45
|
+
endpoint: '/test-assets/menu/menu-root.json',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// click our tasks
|
|
49
|
+
menu.getDiv('#menu-tasks').click();
|
|
50
|
+
await menu.httpComplete;
|
|
51
|
+
menu.requestUpdate();
|
|
52
|
+
// await menu.updateComplete;
|
|
53
|
+
|
|
54
|
+
expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);
|
|
55
|
+
await assertScreenshot('menu/menu-submenu', getClip(menu));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('sets focus', async () => {
|
|
59
|
+
// setting focus just shows the selection, it does
|
|
60
|
+
// not trigger events such as loading or dispatching
|
|
61
|
+
|
|
62
|
+
const menu: TembaMenu = await getMenu({
|
|
63
|
+
endpoint: '/test-assets/menu/menu-root.json',
|
|
42
64
|
});
|
|
43
65
|
|
|
44
|
-
// click our
|
|
66
|
+
// click our tasks
|
|
45
67
|
menu.getDiv('#menu-tasks').click();
|
|
46
68
|
await menu.httpComplete;
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
// now set the focus manually
|
|
71
|
+
menu.setFocusedItem('schedule');
|
|
72
|
+
|
|
73
|
+
// setting focus does NOT fetch items
|
|
74
|
+
expect(menu.root.items[IDX_SCHEDULE].items).to.equal(undefined);
|
|
75
|
+
|
|
76
|
+
// now load the items explicitly
|
|
77
|
+
menu.getDiv('#menu-schedule').click();
|
|
78
|
+
await menu.httpComplete;
|
|
79
|
+
expect(menu.root.items[IDX_SCHEDULE].items.length).to.equal(3);
|
|
80
|
+
|
|
81
|
+
await assertScreenshot('menu/menu-focused-with items', getClip(menu));
|
|
82
|
+
|
|
83
|
+
menu.setFocusedItem('tasks');
|
|
84
|
+
await assertScreenshot('menu/menu-tasks', getClip(menu));
|
|
85
|
+
|
|
86
|
+
menu.setFocusedItem('tasks/todo');
|
|
87
|
+
await assertScreenshot('menu/menu-tasks-nextup', getClip(menu));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('refreshes', async () => {
|
|
91
|
+
// the menu should refresh along the selection path without destroying state
|
|
92
|
+
const menu: TembaMenu = await getMenu({
|
|
93
|
+
endpoint: '/test-assets/menu/menu-root.json',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// click our tasks
|
|
97
|
+
menu.getDiv('#menu-tasks').click();
|
|
98
|
+
menu.requestUpdate();
|
|
99
|
+
await menu.httpComplete;
|
|
100
|
+
// await menu.updateComplete;
|
|
101
|
+
|
|
102
|
+
// now click on the todo
|
|
103
|
+
menu.getDiv('#menu-todo').click();
|
|
104
|
+
menu.requestUpdate();
|
|
105
|
+
// await menu.updateComplete;
|
|
106
|
+
|
|
107
|
+
expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);
|
|
108
|
+
await assertScreenshot('menu/menu-refresh-1', getClip(menu));
|
|
109
|
+
|
|
110
|
+
// now refresh!
|
|
111
|
+
menu.refresh();
|
|
112
|
+
await menu.httpComplete;
|
|
49
113
|
|
|
50
|
-
|
|
114
|
+
// we should still have our task items
|
|
115
|
+
expect(menu.root.items[IDX_TASKS].items.length).to.equal(3);
|
|
116
|
+
await assertScreenshot('menu/menu-refresh-2', getClip(menu));
|
|
51
117
|
});
|
|
52
118
|
});
|