diff --git a/src/app/remotes/vertical-main-menu/vertical-main-manu.component.scss b/src/app/remotes/vertical-main-menu/vertical-main-manu.component.scss
index 5a4d4a5c..d9abf586 100644
--- a/src/app/remotes/vertical-main-menu/vertical-main-manu.component.scss
+++ b/src/app/remotes/vertical-main-menu/vertical-main-manu.component.scss
@@ -1 +1,42 @@
@import './../../shared/sidebar-panelmenu.scss';
+
+:host ::ng-deep {
+ .p-panelmenu {
+ .p-panelmenu-header {
+ &.ocx-vertical-menu-active-item {
+ &:not(:focus-visible):not(:hover) {
+ > .p-panelmenu-header-content {
+ .p-panelmenu-header-action {
+ &:not(:hover):not(:focus) {
+ background-color: var(--menu-active-item-bg-color);
+ color: var(--menu-active-item-text-color);
+ }
+ }
+ }
+ }
+ }
+ }
+ .p-panelmenu-content {
+ .p-menuitem {
+ &.ocx-vertical-menu-active-item {
+ &:not(.p-focus) {
+ > .p-menuitem-content {
+ &:not(:hover) {
+ .p-menuitem-link {
+ background-color: var(--menu-active-item-bg-color);
+ color: var(--menu-active-item-text-color);
+ .p-menuitem-icon {
+ color: var(--menu-active-item-text-color);
+ }
+ .p-menuitem-text {
+ color: var(--menu-active-item-text-color);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.html b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.html
index 3a05d3be..27789c60 100644
--- a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.html
+++ b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.html
@@ -1 +1 @@
-
+
diff --git a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.spec.ts b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.spec.ts
index bb422da4..2eb239de 100644
--- a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.spec.ts
+++ b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.spec.ts
@@ -138,6 +138,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -183,6 +191,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -224,6 +240,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -265,6 +289,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
const router = TestBed.inject(Router)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -305,6 +337,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -341,7 +381,7 @@ describe('OneCXVerticalMainMenuComponent', () => {
{
key: 'CORE_AH_MGMT',
name: 'Announcement & Help',
- url: '',
+ url: 'page-url',
position: 1,
external: false,
i18n: {},
@@ -373,6 +413,14 @@ describe('OneCXVerticalMainMenuComponent', () => {
)
const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: 'page-url'
+ }
+ })
+ )
await component.ngOnInit()
const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
@@ -388,6 +436,208 @@ describe('OneCXVerticalMainMenuComponent', () => {
expect((await secondItemChildren[1].getChildren()).length).toBe(0)
})
+ describe('on router changes', () => {
+ const baseItems = [
+ {
+ key: 'PORTAL_MAIN_MENU',
+ name: 'Main Menu',
+ children: [
+ {
+ key: 'CORE_WELCOME',
+ name: 'Welcome Page',
+ url: '/admin/welcome',
+ position: 0,
+ external: false,
+ i18n: {},
+ children: []
+ },
+ {
+ key: 'CORE_AH_MGMT',
+ name: 'Announcement & Help',
+ url: 'page-url',
+ position: 1,
+ external: false,
+ i18n: {},
+ children: [
+ {
+ key: 'CORE_AH_MGMT_A',
+ name: 'Announcements',
+ url: '/admin/announcement',
+ position: 1,
+ external: false,
+ i18n: {},
+ children: []
+ },
+ {
+ key: 'CORE_AH_MGMT_HI',
+ name: 'Help Items',
+ url: '/admin/help',
+ position: 2,
+ external: false,
+ i18n: {},
+ children: []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+
+ it('should expand active item parents', async () => {
+ const appStateService = TestBed.inject(AppStateService)
+ spyOn(appStateService.currentWorkspace$, 'asObservable').and.returnValue(
+ of({
+ workspaceName: 'test-workspace'
+ }) as any
+ )
+ spyOn(appStateService.currentMfe$, 'asObservable').and.returnValue(of({} as any))
+ menuItemApiSpy.getMenuItems.and.returnValue(
+ of({
+ workspaceName: 'test-workspace',
+ menu: baseItems
+ } as any)
+ )
+
+ const { fixture, component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: '/admin/help'
+ }
+ })
+ )
+ await component.ngOnInit()
+
+ const menu = await TestbedHarnessEnvironment.harnessForFixture(fixture, PPanelMenuHarness)
+ const panels = await menu.getAllPanels()
+ expect(panels.length).toEqual(2)
+
+ expect((await panels[0].getChildren()).length).toBe(0)
+ const secondItemChildren = await panels[1].getChildren()
+ expect(secondItemChildren.length).toBe(2)
+ expect(await secondItemChildren[0].getText()).toEqual('Announcements')
+ expect(await (await secondItemChildren[0].host()).hasClass(component.activeItemClass)).toBeFalse()
+ expect(await secondItemChildren[1].getText()).toEqual('Help Items')
+ expect(await (await secondItemChildren[1].host()).hasClass(component.activeItemClass)).toBeTrue()
+
+ const menuItems = component.menuItems$.getValue()
+ expect(menuItems?.items.length).toBe(2)
+ expect(menuItems?.items[0].expanded).toBeFalsy()
+ expect(menuItems?.items[1].expanded).toBeTrue()
+ })
+
+ it('should update items if workspace did not change', async () => {
+ const appStateService = TestBed.inject(AppStateService)
+ spyOn(appStateService.currentWorkspace$, 'asObservable').and.returnValue(
+ of({
+ workspaceName: 'test-workspace'
+ }) as any
+ )
+ spyOn(appStateService.currentMfe$, 'asObservable').and.returnValue(of({} as any))
+ menuItemApiSpy.getMenuItems.and.returnValue(
+ of({
+ workspaceName: 'test-workspace',
+ menu: [
+ {
+ key: 'my-item',
+ name: 'item-name-1',
+ url: '/admin/help',
+ position: 2,
+ external: false,
+ i18n: {},
+ children: []
+ }
+ ]
+ } as any)
+ )
+
+ const { component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: '/admin/help'
+ }
+ })
+ )
+ component.menuItems$.next({
+ workspaceName: 'test-workspace',
+ items: [
+ {
+ id: 'my-item',
+ items: undefined,
+ label: 'item-name-2',
+ routerLink: '/admin/help'
+ }
+ ]
+ })
+ await component.ngOnInit()
+
+ const menuItems = component.menuItems$.getValue()
+ expect(menuItems?.items.length).toBe(1)
+ expect(menuItems?.items[0].label).toBe('item-name-2')
+ })
+
+ it('should overwrite items if workspace has changed', async () => {
+ const appStateService = TestBed.inject(AppStateService)
+ spyOn(appStateService.currentWorkspace$, 'asObservable').and.returnValue(
+ of({
+ workspaceName: 'other-workspace'
+ }) as any
+ )
+ spyOn(appStateService.currentMfe$, 'asObservable').and.returnValue(of({} as any))
+ menuItemApiSpy.getMenuItems.and.returnValue(
+ of({
+ workspaceName: 'other-workspace',
+ menu: [
+ {
+ key: 'PORTAL_MAIN_MENU',
+ name: 'Main Menu',
+ children: [
+ {
+ key: 'my-item',
+ name: 'item-name-1',
+ url: '/admin/help',
+ position: 2,
+ external: false,
+ i18n: {},
+ children: []
+ }
+ ]
+ }
+ ]
+ } as any)
+ )
+
+ const { component } = setUp()
+ spyOn(component.eventsTopic$, 'asObservable').and.returnValue(
+ of({
+ type: 'navigated',
+ payload: {
+ url: '/admin/help'
+ }
+ })
+ )
+ component.menuItems$.next({
+ workspaceName: 'test-workspace',
+ items: [
+ {
+ id: 'my-item',
+ items: undefined,
+ label: 'item-name-2',
+ routerLink: '/admin/help'
+ }
+ ]
+ })
+ await component.ngOnInit()
+
+ const menuItems = component.menuItems$.getValue()
+ expect(menuItems?.items.length).toBe(1)
+ expect(menuItems?.items[0].label).toBe('item-name-1')
+ })
+ })
+
it('should return 0 panels when unable to load them', async () => {
const appStateService = TestBed.inject(AppStateService)
spyOn(appStateService.currentWorkspace$, 'asObservable').and.returnValue(
diff --git a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.ts b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.ts
index fd6b18c6..5369a8ca 100644
--- a/src/app/remotes/vertical-main-menu/vertical-main-menu.component.ts
+++ b/src/app/remotes/vertical-main-menu/vertical-main-menu.component.ts
@@ -1,6 +1,6 @@
import { CommonModule, Location } from '@angular/common'
import { HttpClient } from '@angular/common/http'
-import { Component, Inject, Input, OnInit } from '@angular/core'
+import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'
import { RouterModule } from '@angular/router'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'
@@ -12,6 +12,7 @@ import {
ocxRemoteWebcomponent,
provideTranslateServiceForRoot
} from '@onecx/angular-remote-components'
+import { EventsTopic, NavigatedEventPayload } from '@onecx/integration-interface'
import {
AppStateService,
PortalCoreModule,
@@ -20,12 +21,30 @@ import {
} from '@onecx/portal-integration-angular'
import { MenuItem } from 'primeng/api'
import { PanelMenuModule } from 'primeng/panelmenu'
-import { Observable, ReplaySubject, catchError, map, mergeMap, of, retry, shareReplay, withLatestFrom } from 'rxjs'
+import {
+ BehaviorSubject,
+ ReplaySubject,
+ catchError,
+ combineLatest,
+ distinctUntilChanged,
+ filter,
+ map,
+ mergeMap,
+ of,
+ retry,
+ shareReplay,
+ withLatestFrom
+} from 'rxjs'
import { Configuration, MenuItemAPIService } from 'src/app/shared/generated'
import { MenuItemService } from 'src/app/shared/services/menu-item.service'
import { SharedModule } from 'src/app/shared/shared.module'
import { environment } from 'src/environments/environment'
+export interface WorkspaceMenuItems {
+ workspaceName: string
+ items: MenuItem[]
+}
+
@Component({
selector: 'app-vertical-main-menu',
templateUrl: './vertical-main-menu.component.html',
@@ -56,8 +75,14 @@ import { environment } from 'src/environments/environment'
]
})
@UntilDestroy()
-export class OneCXVerticalMainMenuComponent implements ocxRemoteComponent, ocxRemoteWebcomponent, OnInit {
- menuItems$: Observable