Skip to content

Commit

Permalink
feat: improve remote component loading + add first skeleton loaders (#…
Browse files Browse the repository at this point in the history
…226)

* refactor: improve remote component loading and add first skeleton loaders

* refactor: remove unnecessary export

* fix: fix remote component loading + add workspace name property to currentWorkspace/currentPortal

* fix: fix mock

* fix: fix test mocks to match latest workspace model
  • Loading branch information
bastianjakobi authored Apr 23, 2024
1 parent 9c1c97b commit a8fb5c2
Show file tree
Hide file tree
Showing 14 changed files with 75 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe('PageHeaderComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe('SearchHeaderComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class AppStateServiceMock {
globalLoading$ = new FakeTopic(false)
currentMfe$ = new FakeTopic({ mountPath: '/', remoteBaseUrl: '.', baseHref: '/', shellName: 'test' })
currentPage$ = new FakeTopic<PageInfo | undefined>(undefined)
currentPortal$ = new FakeTopic<Workspace>({ baseUrl: '/', microfrontendRegistrations: [], portalName: 'Test portal' })
currentPortal$ = new FakeTopic<Workspace>({ baseUrl: '/', microfrontendRegistrations: [], portalName: 'Test portal', workspaceName: 'Test portal' })
currentWorkspace$ = new FakeTopic<Workspace>({ baseUrl: '/', microfrontendRegistrations: [], portalName: 'Test workspace', workspaceName: 'Test workspace' })
isAuthenticated$ = new FakeTopic<null>(null)
}
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
<div *ngFor="let component of (components$ | async)" #slot></div>
<div *ngFor="let component of (components$ | async);" #slot>
<ng-container *ngIf="skeleton" [ngTemplateOutlet]="skeleton"></ng-container>
</div>
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import {
Component,
ContentChild,
Inject,
Input,
OnDestroy,
OnInit,
QueryList,
TemplateRef,
Type,
ViewChildren,
ViewContainerRef,
} from '@angular/core'
import { BehaviorSubject, Subscription, Observable, combineLatest } from 'rxjs'
import { RemoteComponentInfo, SLOT_SERVICE, SlotService } from '../../services/slot.service'
import { RemoteComponentInfo, SLOT_SERVICE, SlotComponentConfiguration, SlotService } from '../../services/slot.service'
import { ocxRemoteComponent } from '../../model/remote-component'

@Component({
Expand All @@ -27,12 +29,10 @@ export class SlotComponent implements OnInit, OnDestroy {
this._viewContainers$.next(value)
}

@ContentChild('skeleton') skeleton: TemplateRef<any> | undefined

subscription: Subscription | undefined
components$:
| Observable<
{ componentType: Type<unknown> | undefined; remoteComponent: RemoteComponentInfo; permissions: string[] }[]
>
| undefined
components$: Observable<SlotComponentConfiguration[]> | undefined

constructor(@Inject(SLOT_SERVICE) private slotService: SlotService) {}

Expand All @@ -43,23 +43,40 @@ export class SlotComponent implements OnInit, OnDestroy {
if (viewContainers && viewContainers.length === components.length) {
components.forEach((componentInfo, i) => {
if (componentInfo.componentType) {
const componentRef = viewContainers.get(i)?.createComponent<any>(componentInfo.componentType)
if (componentRef && 'ocxInitRemoteComponent' in componentRef.instance) {
;(componentRef.instance as ocxRemoteComponent).ocxInitRemoteComponent({
appId: componentInfo.remoteComponent.appId,
productName: componentInfo.remoteComponent.productName,
baseUrl: componentInfo.remoteComponent.baseUrl,
permissions: componentInfo.permissions,
})
}
componentRef?.changeDetectorRef.detectChanges()
Promise.all([Promise.resolve(componentInfo.componentType), Promise.resolve(componentInfo.permissions)]).then(([componentType, permissions]) => {
this.createComponent(componentType, componentInfo, permissions, viewContainers, i)
})
}
})
}
}
)
}

private createComponent(
componentType: Type<unknown> | undefined,
componentInfo: { remoteComponent: RemoteComponentInfo; },
permissions: string[],
viewContainers: QueryList<ViewContainerRef>,
i: number
) {
const viewContainer = viewContainers.get(i);
viewContainer?.clear()
viewContainer?.element.nativeElement.replaceChildren()
if (componentType) {
const componentRef = viewContainer?.createComponent<any>(componentType)
if (componentRef && 'ocxInitRemoteComponent' in componentRef.instance) {
;(componentRef.instance as ocxRemoteComponent).ocxInitRemoteComponent({
appId: componentInfo.remoteComponent.appId,
productName: componentInfo.remoteComponent.productName,
baseUrl: componentInfo.remoteComponent.baseUrl,
permissions: permissions,
})
}
componentRef?.changeDetectorRef.detectChanges()
}
}

ngOnDestroy(): void {
this.subscription?.unsubscribe()
}
Expand Down
10 changes: 7 additions & 3 deletions libs/angular-remote-components/src/lib/services/slot.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ export const SLOT_SERVICE: InjectionToken<SlotService> = new InjectionToken('SLO

export type RemoteComponentInfo = { appId: string; productName: string; baseUrl: string }

export type SlotComponentConfiguration = {
componentType: Promise<Type<unknown> | undefined> | Type<unknown> | undefined;
remoteComponent: RemoteComponentInfo;
permissions: Promise<string[]> | string[];
}

export interface SlotService {
init(): Promise<void>
getComponentsForSlot(
slotName: string
): Observable<
{ componentType: Type<unknown> | undefined; remoteComponent: RemoteComponentInfo; permissions: string[] }[]
>
): Observable<SlotComponentConfiguration[]>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Workspace {
* @deprecated will be renamed to workspaceName
*/
portalName: string
workspaceName: string
/**
* @deprecated will be removed
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('AnnouncementBannerComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('PortalFooterComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ describe('PortalViewportComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('SearchCriteriaComponent', () => {
await appStateService.currentPortal$.publish({
id: 'i-am-test-portal',
portalName: 'test',
workspaceName: 'test',
baseUrl: '',
microfrontendRegistrations: [],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export function standaloneInitializer(
throw e
}
console.log(`📃 portal OK? `, portal)
await appStateService.currentPortal$.publish(portal)
await appStateService.currentPortal$.publish({
...portal,
workspaceName: portal.portalName
})

const standaloneMfeInfo: MfeInfo = {
mountPath: '/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@
</svg>
</a>

<a class="layout-menu-button shadow-6" (click)="onMenuButtonClick($event)" pRipple [title]="menuButtonTitle ?? ('OCX_HEADER.MENU_TOGGLE')">
<a
class="layout-menu-button shadow-6"
(click)="onMenuButtonClick($event)"
pRipple
[title]="menuButtonTitle ?? ('OCX_HEADER.MENU_TOGGLE')"
>
<i class="pi pi-chevron-right"></i>
</a>
</div>
Expand All @@ -44,17 +49,21 @@
<ng-content></ng-content>
</div>
<div class="layout-topbar-actions-right">
<ocx-slot name="headerRight" class="layout-topbar-items"></ocx-slot>
<ocx-slot name="headerRight" class="layout-topbar-items">
<div class="flex flex-row justify-content-between w-full gap-3 h-full">
<ng-template #skeleton>
<div class="flex align-items-center h-full justify-content-center" style="width: 56px;">
<p-skeleton shape="circle" size="2.5rem" class="h-full flex align-items-center"></p-skeleton>
</div>
</ng-template>
</div>
</ocx-slot>
<ul class="layout-topbar-items">
<!-- Only desktop: Actions (favorites, support, search, ...) as icon buttons -->
<ng-container *ocxIfBreakpoint="'desktop'">

</ng-container>
<ng-container *ocxIfBreakpoint="'desktop'"> </ng-container>

<!-- Only mobile: Actions (favorites, support, search, ...) as 'more' button + overlay -->
<li class="layout-topbar-item" *ocxIfBreakpoint="'mobile'">

</li>
<li class="layout-topbar-item" *ocxIfBreakpoint="'mobile'"></li>
</ul>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion libs/shell-core/src/lib/shell-core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { GlobalErrorComponent } from './components/error-component/global-error.
import { PortalFooterComponent } from './components/portal-footer/portal-footer.component'
import { HeaderComponent } from './components/portal-header/header.component'
import { PortalViewportComponent } from './components/portal-viewport/portal-viewport.component'
import { SkeletonModule } from 'primeng/skeleton'

@NgModule({
imports: [CommonModule, RouterModule, AngularRemoteComponentsModule, AngularAcceleratorModule, ToastModule],
imports: [CommonModule, RouterModule, AngularRemoteComponentsModule, AngularAcceleratorModule, ToastModule, SkeletonModule],
declarations: [PortalViewportComponent, HeaderComponent, PortalFooterComponent, GlobalErrorComponent],
exports: [PortalViewportComponent, HeaderComponent, PortalFooterComponent, ToastModule, GlobalErrorComponent],
})
Expand Down

0 comments on commit a8fb5c2

Please sign in to comment.