Skip to content

Commit

Permalink
feat: new shell libraries (#195)
Browse files Browse the repository at this point in the history
* feat: create new libs

* feat: init new shell libraries

* fix: PrimeIcons not assignable to string for button

* fix: change selector prefix to ocx

* fix: minor change

* feat: added topic for remote-components

* fix: fixed error in model of remote-component topic

* feat: moved slot component to angular-remote-components

* feat: SlotService returns config and type

* fix: fixed build error

* fix: fixed build error

* feat: provided SLOT_SERVICE in AngularStandaloneShellModule

* feat: added standalone module to PIA

* feat: added permissions to slot.component

* fix: renamed basePath ot baseUrl

* feat: add shell-portal-viewport,  -portal-header, -portal-footer

* feat: added image in header

* fix: fixed name of AngularRemoteComponentsModule

* feat: appConfig- and permissionsCache-service

* feat: eventsTopic, topicPublisher, create remote translate loader, bffurl removed

* feat: rename currentPortal to currentWorkspace, deprecate some variables

* feat: provide translate service for root

* feat: fix issue with auth, set favicon, fix slot component

* fix: remove buildCopy script

* feat: move advanced directive, fix lint issues

* fix: npm i

* feat: delete angular standalone shell, permision directive, some fixes

* fix: permission directive

* fix: clientSideSorting

* fix: shell bff prefix in uppercase

* fix: remove try catch block

* fix: lint issues

* feat: remove tests for shell header, footer, portal viewport

* fix: change from portal to workspace

---------

Co-authored-by: kim.tran <[email protected]>
Co-authored-by: Jan-Gerrit Schettler-Köhler <[email protected]>
  • Loading branch information
3 people authored Apr 3, 2024
1 parent ad0ebb4 commit 4a8a127
Show file tree
Hide file tree
Showing 85 changed files with 28,387 additions and 18,747 deletions.
1 change: 1 addition & 0 deletions libs/accelerator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './lib/topic/topic'
export * from './lib/topic/syncable-topic'
export * from './lib/topic/topic-publisher'
17 changes: 17 additions & 0 deletions libs/accelerator/src/lib/topic/topic-publisher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TopicDataMessage } from './topic-data-message'
import { TopicMessageType } from './topic-message-type'

export class TopicPublisher<T> {
protected publishPromiseResolver: Record<number, () => void> = {}

constructor(public name: string, public version: number) {}

public publish(value: T): Promise<void> {
const message = new TopicDataMessage<T>(TopicMessageType.TopicNext, this.name, this.version, value)
const promise = new Promise<void>((resolve) => {
this.publishPromiseResolver[message.timestamp] = resolve
})
window.postMessage(message, '*')
return promise
}
}
24 changes: 10 additions & 14 deletions libs/accelerator/src/lib/topic/topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,28 @@ import {
import { TopicDataMessage } from './topic-data-message'
import { TopicMessage } from './topic-message'
import { TopicMessageType } from './topic-message-type'
import { TopicPublisher } from './topic-publisher'

export class Topic<T> implements Subscribable<T> {
export class Topic<T> extends TopicPublisher<T> implements Subscribable<T> {
protected isInitializedPromise: Promise<void>
protected data = new BehaviorSubject<TopicDataMessage<T> | undefined>(undefined)

protected isInit = false
private resolveInitPromise!: (value: void | PromiseLike<void>) => void
private eventListener = (m: MessageEvent<TopicMessage>) => this.onMessage(m)
private publishPromiseResolver: Record<number, () => void> = {}

constructor(private name: string, private version: number) {
constructor(name: string, version: number, sendGetMessage = true) {
super(name, version)

this.isInitializedPromise = new Promise<void>((resolve) => {
this.resolveInitPromise = resolve
})
window.addEventListener('message', this.eventListener)
const message = new TopicMessage(TopicMessageType.TopicGet, this.name, this.version)
window.postMessage(message, '*')

if (sendGetMessage) {
const message = new TopicMessage(TopicMessageType.TopicGet, this.name, this.version)
window.postMessage(message, '*')
}
}

get isInitialized(): Promise<void> {
Expand Down Expand Up @@ -133,15 +138,6 @@ export class Topic<T> implements Subscribable<T> {
return (<any>this.asObservable()).pipe(...operations)
}

publish(value: T): Promise<void> {
const message = new TopicDataMessage<T>(TopicMessageType.TopicNext, this.name, this.version, value)
const promise = new Promise<void>((resolve) => {
this.publishPromiseResolver[message.timestamp] = resolve
})
window.postMessage(message, '*')
return promise
}

destroy() {
window.removeEventListener('message', this.eventListener, true)
}
Expand Down
3 changes: 3 additions & 0 deletions libs/angular-accelerator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export * from './lib/directives/if-permission.directive'
export * from './lib/directives/if-breakpoint.directive'
export * from './lib/directives/src.directive'
export * from './lib/directives/advanced.directive'

// components
export * from './lib/components/column-group-selection/column-group-selection.component'
Expand All @@ -21,6 +22,7 @@ export * from './lib/components/search-header/search-header.component'
// services
export * from './lib/services/breadcrumb.service'
export * from './lib/services/translation-cache.service'
export * from './lib/services/app-config-service'

// pipes
export * from './lib/pipes/dynamic.pipe'
Expand Down Expand Up @@ -54,3 +56,4 @@ export * from './lib/utils/dateutils'
export * from './lib/utils/objectutils'
export * from './lib/utils/primeicon.utils'
export * from './lib/utils/translate.combined.loader'
export * from './lib/utils/create-remote-component-translate-loader.utils'
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { MenuModule } from 'primeng/menu'
import { ChartModule } from 'primeng/chart'
import { MultiSelectModule } from 'primeng/multiselect'
import { BreadcrumbModule } from 'primeng/breadcrumb'
import { SkeletonModule } from 'primeng/skeleton';
import { MessageModule } from 'primeng/message';

@NgModule({
imports: [
Expand All @@ -24,6 +26,8 @@ import { BreadcrumbModule } from 'primeng/breadcrumb'
MenuModule,
ChartModule,
MultiSelectModule,
SkeletonModule,
MessageModule
],
exports: [
BreadcrumbModule,
Expand All @@ -37,6 +41,8 @@ import { BreadcrumbModule } from 'primeng/breadcrumb'
MenuModule,
ChartModule,
MultiSelectModule,
SkeletonModule,
MessageModule
],
})
export class AngularAcceleratorPrimeNgModule {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common'
import { CUSTOM_ELEMENTS_SCHEMA, LOCALE_ID, NgModule } from '@angular/core'
import { LOCALE_ID, NgModule } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterModule } from '@angular/router'
import { HttpClient } from '@angular/common/http'
Expand Down Expand Up @@ -33,6 +33,7 @@ import { OcxTimeAgoPipe } from './pipes/ocxtimeago.pipe'
import { createTranslateLoader } from './utils/create-translate-loader.utils'
import { TranslationCacheService } from './services/translation-cache.service'
import { SrcDirective } from './directives/src.directive'
import { AdvancedDirective } from './directives/advanced.directive'

export class AngularAcceleratorMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
Expand Down Expand Up @@ -80,6 +81,8 @@ export class AngularAcceleratorMissingTranslationHandler implements MissingTrans
IfBreakpointDirective,
SrcDirective,
OcxTimeAgoPipe,
SrcDirective,
AdvancedDirective
],
providers: [
{
Expand Down Expand Up @@ -107,8 +110,9 @@ export class AngularAcceleratorMissingTranslationHandler implements MissingTrans
IfBreakpointDirective,
SrcDirective,
OcxTimeAgoPipe,
SrcDirective,
AdvancedDirective,
// DataListGridSortingComponent,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AngularAcceleratorModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
appendTo="body"
(onClick)="onFilterClick(column)"
[title]="'OCX_DATA_TABLE.FILTER_TITLE' | translate"
aria-label="{{('OCX_DATA_TABLE.FILTER_ARIA_LABEL' | translate: { column: column.nameKey | translate})}}"
[attr.aria-label]="('OCX_DATA_TABLE.FILTER_ARIA_LABEL' | translate: { column: column.nameKey | translate})"
>
<ng-template pTemplate="selectedItems" let-value>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
[sortDirection]="sortDirection"
[sortField]="sortField"
[sortStates]="sortStates"
[clientSideFiltering]="clientSideFiltering"
[clientSideSorting]="clientSideSorting"
[titleLineId]="titleLineId || firstColumnId"
[subtitleLineIds]="subtitleLineIds"
[clientSideSorting]="true"
Expand Down Expand Up @@ -70,8 +72,8 @@
[sortDirection]="sortDirection"
[sortColumn]="sortField"
[sortStates]="sortStates"
[clientSideFiltering]="true"
[clientSideSorting]="true"
[clientSideFiltering]="clientSideFiltering"
[clientSideSorting]="clientSideSorting"
[pageSizes]="pageSizes"
[pageSize]="pageSize"
[paginator]="tablePaginator"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class DataViewComponent implements DoCheck, OnInit {
@Input() layout: any = ['grid', 'list', 'table']
@Input() columns: DataTableColumn[] = []
@Input() emptyResultsMessage: string | undefined
@Input() clientSideSorting = true
@Input() clientSideFiltering = true
@Input() fallbackImage = 'placeholder.png'
@Input() filters: Filter[] = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
[sortDirection]="sortDirection"
[titleLineId]="titleLineId || firstColumnId"
[subtitleLineIds]="subtitleLineIds"
[clientSideSorting]="true"
[clientSideFiltering]="true"
[clientSideSorting]="clientSideSorting"
[clientSideFiltering]="clientSideFiltering"
[pageSizes]="pageSizes"
[pageSize]="pageSize"
[emptyResultsMessage]="emptyResultsMessage"
Expand All @@ -75,7 +75,7 @@
[stringTableCellTemplate]="_stringTableCell ? stringTableCell : undefined"
[numberTableCellTemplate]="_numberTableCell ? numberTableCell : undefined"
[tableDateCellTemplate]="_tableDateCell ? tableDateCell : undefined"
[tableRelativeDateTemplate]="_tableRelativeDateCell ? tableRelativeDateCell : undefined"
[tableRelativeDateCellTemplate]="_tableRelativeDateCell ? tableRelativeDateCell : undefined"
[tableCellTemplate]="_tableCell ? tableCell : undefined"
[tableTranslationKeyCellTemplate]="_tableTranslationKeyCell ? tableTranslationKeyCell : undefined"
[gridItemSubtitleLinesTemplate]="_gridItemSubtitleLines ? gridItemSubtitleLines : undefined"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class InteractiveDataViewComponent implements OnInit {
@Input() supportedViewLayouts: ('grid' | 'list' | 'table')[] = ['grid', 'list', 'table']
@Input() columns: DataTableColumn[] = []
@Input() emptyResultsMessage: string | undefined
@Input() clientSideSorting = true
@Input() clientSideFiltering = true
@Input() fallbackImage = 'placeholder.png'
@Input() filters: Filter[] = []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Directive, DoCheck, Optional, TemplateRef, ViewContainerRef } from '@angular/core'
import { SearchHeaderComponent } from '@onecx/angular-accelerator'
import { SearchHeaderComponent } from '../components/search-header/search-header.component'

@Directive({ selector: '[ocxAdvanced]' })
export class AdvancedDirective implements DoCheck {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ export class IfPermissionDirective implements OnInit {
this.permission = value
this.negate = true
}

@Input() onMissingPermission: 'hide' | 'disable' = 'hide'

@Input() ocxIfPermissionPermissions: string[] | undefined
@Input()
set ocxIfNotPermissionPermissions(value: string[] | undefined) {
this.ocxIfPermissionPermissions = value
}

negate = false

constructor(
Expand All @@ -22,7 +29,7 @@ export class IfPermissionDirective implements OnInit {

ngOnInit() {
if (this.permission) {
if (this.negate === this.userService.hasPermission(this.permission)) {
if (this.negate === this.hasPermission(this.permission)) {
if (this.onMissingPermission === 'disable') {
this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'disabled')
} else {
Expand All @@ -35,4 +42,11 @@ export class IfPermissionDirective implements OnInit {
}
}
}

hasPermission(permission: string) {
if (this.ocxIfPermissionPermissions) {
return this.ocxIfPermissionPermissions.includes(permission)
}
return this.userService.hasPermission(permission)
}
}
41 changes: 41 additions & 0 deletions libs/angular-accelerator/src/lib/services/app-config-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Config } from '@onecx/integration-interface'
import { BehaviorSubject, firstValueFrom } from 'rxjs'

@Injectable()
export class AppConfigService {
config$ = new BehaviorSubject<{ [key: string]: string }>({})

constructor(private http: HttpClient) {}

public init(baseUrl: string): Promise<void> {
return new Promise((resolve, reject) => {
const loadConfigPromise: Promise<Config> = firstValueFrom(this.http.get<Config>(baseUrl + 'assets/env.json'))

loadConfigPromise
.then(async (config) => {
if (config) {
this.config$.next(config)
resolve()
}
})
.catch((e) => {
console.log(`Failed to load env configuration`)
reject(e)
})
})
}

public getProperty(key: string): string | undefined {
return this.config$.getValue()?.[key]
}

public setProperty(key: string, val: string) {
this.config$.next({ ...this.config$.value, [key]: val })
}

public getConfig(): { [key: string]: string } {
return this.config$.getValue()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Location } from '@angular/common'
import { HttpClient } from '@angular/common/http'
import { inject } from '@angular/core'
import { TranslateLoader } from '@ngx-translate/core'
import { map, ReplaySubject, tap } from 'rxjs'
import { TranslationCacheService } from '../services/translation-cache.service'
import { AsyncTranslateLoader } from './async-translate-loader.utils'
import { CachingTranslateLoader } from './caching-translate-loader.utils'
import { TranslateCombinedLoader } from './translate.combined.loader'

let lastTranslateLoaderTimerId = 0

export function createRemoteComponentTranslateLoader(
http: HttpClient,
baseUrlReplaySubject$: ReplaySubject<string>,
translationCacheService?: TranslationCacheService
): TranslateLoader {
const ts = translationCacheService ?? inject(TranslationCacheService)
const timerId = lastTranslateLoaderTimerId++

console.time('createRemoteComponentTranslateLoader_' + timerId)
return new AsyncTranslateLoader(
baseUrlReplaySubject$.pipe(
map((baseUrl) => {
return new TranslateCombinedLoader(
// translations of shell or of app in standalone mode
new CachingTranslateLoader(ts, http, `./assets/i18n/`, '.json'),
// translations of portal-integration-angular of app
new CachingTranslateLoader(
ts,
http,
Location.joinWithSlash(baseUrl, `onecx-portal-lib/assets/i18n/`),
'.json'
),
// translations of the app
new CachingTranslateLoader(ts, http, Location.joinWithSlash(baseUrl, `assets/i18n/`), '.json')
)
}),
tap(() => console.timeEnd('createRemoteComponentTranslateLoader_' + timerId))
)
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { PageInfo, Portal } from '@onecx/integration-interface'
import { PageInfo, Workspace } from '@onecx/integration-interface'
// eslint-disable-next-line
import { AppStateService } from '@onecx/angular-integration-interface'
import { FakeTopic } from './fake-topic'
Expand All @@ -21,6 +21,6 @@ export class AppStateServiceMock {
globalLoading$ = new FakeTopic(false)
currentMfe$ = new FakeTopic({ mountPath: '/', remoteBaseUrl: '.', baseHref: '/', shellName: 'test' })
currentPage$ = new FakeTopic<PageInfo | undefined>(undefined)
currentPortal$ = new FakeTopic<Portal>({ baseUrl: '/', microfrontendRegistrations: [], portalName: 'Test portal' })
currentPortal$ = new FakeTopic<Workspace>({ baseUrl: '/', microfrontendRegistrations: [], portalName: 'Test portal' })
isAuthenticated$ = new FakeTopic<null>(null)
}
1 change: 1 addition & 0 deletions libs/angular-integration-interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './lib/services/configuration.service'
export * from './lib/services/user.service'
export * from './lib/services/portal-message.service'
export * from './lib/services/theme.service'
export * from './lib/services/remote-components.service'

// models
export * from './lib/model/config-key.model'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @deprecated
*/
export interface IAuthService {
logout(): void

Expand Down
Loading

0 comments on commit 4a8a127

Please sign in to comment.