Skip to content

Commit

Permalink
feat(admin-ui): Allow overriding built-in nav menu items
Browse files Browse the repository at this point in the history
Closes #562
  • Loading branch information
michaelbromley committed Nov 23, 2020
1 parent 4564d79 commit 9d862c6
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ Running the server will compile our new shared module into the app, and the resu

{{< figure src="./ui-extensions-navbar.jpg" >}}

## Overriding existing items

It is also possible to override one of the default (built-in) nav menu sections or items. This can be useful for example if you wish to provide a completely different implementation of the product list view.

This is done by setting the `id` property to that of an existing nav menu section or item.


## Adding new ActionBar buttons

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/* tslint:disable:no-non-null-assertion no-console */
import { TestBed } from '@angular/core/testing';
import { take } from 'rxjs/operators';

import { NavMenuSection } from './nav-builder-types';
import { NavBuilderService } from './nav-builder.service';

describe('NavBuilderService', () => {
let service: NavBuilderService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(NavBuilderService);
});

it('defineNavMenuSections', done => {
service.defineNavMenuSections(getBaseNav());

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(result).toEqual(getBaseNav());
done();
});
});

describe('addNavMenuSection', () => {
it('adding new section to end', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuSection({
id: 'reports',
label: 'Reports',
items: [],
});

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(result.map(section => section.id)).toEqual(['catalog', 'sales', 'reports']);
done();
});
});

it('adding new section before', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuSection(
{
id: 'reports',
label: 'Reports',
items: [],
},
'sales',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(result.map(section => section.id)).toEqual(['catalog', 'reports', 'sales']);
done();
});
});

it('replacing an existing section', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuSection({
id: 'sales',
label: 'Custom Sales',
items: [],
});

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(result.map(section => section.id)).toEqual(['catalog', 'sales']);
expect(result[1].label).toBe('Custom Sales');
done();
});
});

it('replacing and moving', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuSection(
{
id: 'sales',
label: 'Custom Sales',
items: [],
},
'catalog',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(result.map(section => section.id)).toEqual(['sales', 'catalog']);
expect(result[0].label).toBe('Custom Sales');
done();
});
});
});

describe('addNavMenuItem()', () => {
it('adding to non-existent section', done => {
spyOn(console, 'error');
service.defineNavMenuSections(getBaseNav());
service.addNavMenuItem(
{
id: 'fulfillments',
label: 'Fulfillments',
routerLink: ['/extensions', 'fulfillments'],
},
'farm-tools',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
expect(console.error).toHaveBeenCalledWith(
'Could not add menu item "fulfillments", section "farm-tools" does not exist',
);
done();
});
});

it('adding to end of section', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuItem(
{
id: 'fulfillments',
label: 'Fulfillments',
routerLink: ['/extensions', 'fulfillments'],
},
'sales',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
const salesSection = result.find(r => r.id === 'sales')!;

expect(salesSection.items.map(item => item.id)).toEqual(['orders', 'fulfillments']);
done();
});
});

it('adding before existing item', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuItem(
{
id: 'fulfillments',
label: 'Fulfillments',
routerLink: ['/extensions', 'fulfillments'],
},
'sales',
'orders',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
const salesSection = result.find(r => r.id === 'sales')!;

expect(salesSection.items.map(item => item.id)).toEqual(['fulfillments', 'orders']);
done();
});
});

it('replacing existing item', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuItem(
{
id: 'facets',
label: 'Custom Facets',
routerLink: ['/custom-facets'],
},
'catalog',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
const catalogSection = result.find(r => r.id === 'catalog')!;

expect(catalogSection.items.map(item => item.id)).toEqual(['products', 'facets']);
expect(catalogSection.items[1].label).toBe('Custom Facets');
done();
});
});

it('replacing existing item and moving', done => {
service.defineNavMenuSections(getBaseNav());
service.addNavMenuItem(
{
id: 'facets',
label: 'Custom Facets',
routerLink: ['/custom-facets'],
},
'catalog',
'products',
);

service.navMenuConfig$.pipe(take(1)).subscribe(result => {
const catalogSection = result.find(r => r.id === 'catalog')!;

expect(catalogSection.items.map(item => item.id)).toEqual(['facets', 'products']);
expect(catalogSection.items[0].label).toBe('Custom Facets');
done();
});
});
});

function getBaseNav(): NavMenuSection[] {
return [
{
id: 'catalog',
label: 'Catalog',
items: [
{
id: 'products',
label: 'Products',
icon: 'library',
routerLink: ['/catalog', 'products'],
},
{
id: 'facets',
label: 'Facets',
icon: 'tag',
routerLink: ['/catalog', 'facets'],
},
],
},
{
id: 'sales',
label: 'Sales',
requiresPermission: 'ReadOrder',
items: [
{
id: 'orders',
label: 'Orders',
routerLink: ['/orders'],
icon: 'shopping-cart',
},
],
},
];
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ export class NavBuilderService {
* move the section before any existing section with the specified id. If
* omitted (or if the id is not found) the section will be appended to the
* existing set of sections.
*
* Providing the `id` of an existing section will replace that section.
*/
addNavMenuSection(config: NavMenuSection, before?: string) {
this.addedNavMenuSections.push({ config, before });
Expand All @@ -171,6 +173,9 @@ export class NavBuilderService {
* Providing the `before` argument will move the item before any existing item with the specified id.
* If omitted (or if the name is not found) the item will be appended to the
* end of the section.
*
* Providing the `id` of an existing item in that section will replace
* that item.
*/
addNavMenuItem(config: NavMenuItem, sectionId: string, before?: string) {
this.addedNavMenuItems.push({ config, sectionId, before });
Expand Down Expand Up @@ -208,10 +213,17 @@ export class NavBuilderService {
if (!config.requiresPermission) {
config.requiresPermission = Permission.Authenticated;
}
const index = initialConfig.findIndex(c => c.id === before);
if (-1 < index) {
initialConfig.splice(index, 0, config);
} else {
const existingIndex = initialConfig.findIndex(c => c.id === config.id);
if (-1 < existingIndex) {
initialConfig[existingIndex] = config;
}
const beforeIndex = initialConfig.findIndex(c => c.id === before);
if (-1 < beforeIndex) {
if (-1 < existingIndex) {
initialConfig.splice(existingIndex, 1);
}
initialConfig.splice(beforeIndex, 0, config);
} else if (existingIndex === -1) {
initialConfig.push(config);
}
}
Expand All @@ -230,11 +242,19 @@ export class NavBuilderService {
`Could not add menu item "${item.config.id}", section "${item.sectionId}" does not exist`,
);
} else {
const index = section.items.findIndex(i => i.id === item.before);
if (-1 < index) {
section.items.splice(index, 0, item.config);
} else {
section.items.push(item.config);
const { config, sectionId, before } = item;
const existingIndex = section.items.findIndex(i => i.id === config.id);
if (-1 < existingIndex) {
section.items[existingIndex] = config;
}
const beforeIndex = section.items.findIndex(i => i.id === before);
if (-1 < beforeIndex) {
if (-1 < existingIndex) {
section.items.splice(existingIndex, 1);
}
section.items.splice(beforeIndex, 0, config);
} else if (existingIndex === -1) {
section.items.push(config);
}
}
}
Expand Down

0 comments on commit 9d862c6

Please sign in to comment.