diff --git a/libs/angular-accelerator/src/index.ts b/libs/angular-accelerator/src/index.ts
index 9d8ae9d3..87ae2c0a 100644
--- a/libs/angular-accelerator/src/index.ts
+++ b/libs/angular-accelerator/src/index.ts
@@ -17,14 +17,12 @@ export * from './lib/components/diagram/diagram.component'
export * from './lib/components/group-by-count-diagram/group-by-count-diagram.component'
export * from './lib/components/interactive-data-view/interactive-data-view.component'
export * from './lib/components/page-header/page-header.component'
-export * from './lib/components/search-config/search-config.component'
export * from './lib/components/search-header/search-header.component'
export * from './lib/components/data-loading-error/data-loading-error.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'
@@ -40,7 +38,6 @@ export * from './lib/model/data-table-column.model'
export * from './lib/model/diagram-column'
// export * from './lib/model/diagram-data'
export * from './lib/model/diagram-type'
-export * from './lib/model/search-config-info'
// core
export * from './lib/angular-accelerator.module'
@@ -59,6 +56,7 @@ 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-and-mfe-translate-loader.utils'
export * from './lib/utils/create-remote-component-translate-loader.utils'
export * from './lib/utils/enum-to-dropdown-options.utils'
export * from './lib/utils/criteria.utils'
diff --git a/libs/angular-accelerator/src/lib/angular-accelerator.module.ts b/libs/angular-accelerator/src/lib/angular-accelerator.module.ts
index a9291f73..de1e8bd1 100644
--- a/libs/angular-accelerator/src/lib/angular-accelerator.module.ts
+++ b/libs/angular-accelerator/src/lib/angular-accelerator.module.ts
@@ -4,7 +4,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterModule } from '@angular/router'
import { MissingTranslationHandler, MissingTranslationHandlerParams, TranslateModule } from '@ngx-translate/core'
-import { UserService } from '@onecx/angular-integration-interface'
+import { AppConfigService, UserService } from '@onecx/angular-integration-interface'
+import { AngularRemoteComponentsModule } from '@onecx/angular-remote-components'
import { firstValueFrom, skip } from 'rxjs'
import { AngularAcceleratorPrimeNgModule } from './angular-accelerator-primeng.module'
@@ -20,7 +21,6 @@ import { GroupByCountDiagramComponent } from './components/group-by-count-diagra
import { InteractiveDataViewComponent } from './components/interactive-data-view/interactive-data-view.component'
import { PageHeaderComponent } from './components/page-header/page-header.component'
import { DataLoadingErrorComponent } from './components/data-loading-error/data-loading-error.component'
-import { SearchConfigComponent } from './components/search-config/search-config.component'
import { SearchHeaderComponent } from './components/search-header/search-header.component'
import { AdvancedDirective } from './directives/advanced.directive'
import { IfBreakpointDirective } from './directives/if-breakpoint.directive'
@@ -29,7 +29,6 @@ import { SrcDirective } from './directives/src.directive'
import { TooltipOnOverflowDirective } from './directives/tooltipOnOverflow.directive'
import { DynamicPipe } from './pipes/dynamic.pipe'
import { OcxTimeAgoPipe } from './pipes/ocxtimeago.pipe'
-import { AppConfigService } from './services/app-config-service'
import { DynamicLocaleId } from './utils/dynamic-locale-id'
export class AngularAcceleratorMissingTranslationHandler implements MissingTranslationHandler {
@@ -49,6 +48,7 @@ function appInitializer(userService: UserService) {
imports: [
CommonModule,
AngularAcceleratorPrimeNgModule,
+ AngularRemoteComponentsModule,
TranslateModule,
FormsModule,
RouterModule,
@@ -63,7 +63,6 @@ function appInitializer(userService: UserService) {
DataTableComponent,
DataViewComponent,
InteractiveDataViewComponent,
- SearchConfigComponent,
PageHeaderComponent,
DynamicPipe,
SearchHeaderComponent,
@@ -96,6 +95,7 @@ function appInitializer(userService: UserService) {
AppConfigService,
],
exports: [
+ AngularRemoteComponentsModule,
ColumnGroupSelectionComponent,
CustomGroupColumnSelectorComponent,
DataLayoutSelectionComponent,
@@ -103,7 +103,6 @@ function appInitializer(userService: UserService) {
DataTableComponent,
DataViewComponent,
InteractiveDataViewComponent,
- SearchConfigComponent,
PageHeaderComponent,
SearchHeaderComponent,
DiagramComponent,
diff --git a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.html b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.html
index d8377188..68355773 100644
--- a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.html
+++ b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.html
@@ -23,18 +23,32 @@
>
-
-
+
+
+
+
+
+
{
const mutationObserverMock = jest.fn(function MutationObserver(callback) {
@@ -63,6 +67,7 @@ describe('InteractiveDataViewComponent', () => {
let deleteItemEvent: RowListGridData | undefined
let dateUtils: DateUtils
+ let slotService: SlotServiceMock
const mockData = [
{
@@ -229,6 +234,7 @@ describe('InteractiveDataViewComponent', () => {
DataViewComponent,
ColumnGroupSelectionComponent,
CustomGroupColumnSelectorComponent,
+ IfPermissionDirective,
],
imports: [
TranslateModule.forRoot(),
@@ -244,6 +250,10 @@ describe('InteractiveDataViewComponent', () => {
],
providers: [
{ provide: UserService, useClass: MockUserService },
+ {
+ provide: SlotService,
+ useClass: SlotServiceMock,
+ },
provideHttpClient(withInterceptorsFromDi()),
provideRouter([]),
provideAppStateServiceMock(),
@@ -256,6 +266,7 @@ describe('InteractiveDataViewComponent', () => {
component.editPermission = 'TEST_MGMT#TEST_EDIT'
component.deletePermission = 'TEST_MGMT#TEST_DELETE'
component.defaultGroupKey = 'PREDEFINED_GROUP.DEFAULT'
+ component.searchConfigPermission = 'PRODUCT#USE_SEARCHCONFIG'
component.viewItem.subscribe((event) => (viewItemEvent = event))
component.editItem.subscribe((event) => (editItemEvent = event))
component.deleteItem.subscribe((event) => (deleteItemEvent = event))
@@ -270,6 +281,7 @@ describe('InteractiveDataViewComponent', () => {
interactiveDataViewHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, InteractiveDataViewHarness)
dateUtils = TestBed.inject(DateUtils)
+ slotService = TestBed.inject(SlotService) as any as SlotServiceMock
viewItemEvent = undefined
editItemEvent = undefined
@@ -290,9 +302,26 @@ describe('InteractiveDataViewComponent', () => {
expect(dataLayoutSelection).toBeTruthy()
})
+ it('should load column-group-selection slot', async () => {
+ slotService.assignComponentToSlot('column-group-selection', component.columnGroupSlotName)
+ const userService = TestBed.inject(UserService)
+ jest.spyOn(userService, 'hasPermission').mockReturnValue(true)
+ fixture.detectChanges()
+
+ const slot = await loader.getHarness(SlotHarness)
+ expect(slot).toBeTruthy()
+ })
+
it('should load ColumnGroupSelectionDropdown', async () => {
const columnGroupSelectionDropdown = await loader.getHarness(ColumnGroupSelectionHarness)
expect(columnGroupSelectionDropdown).toBeTruthy()
+
+ slotService.assignComponentToSlot('column-group-selection', component.columnGroupSlotName)
+ const userService = TestBed.inject(UserService)
+ jest.spyOn(userService, 'hasPermission').mockReturnValue(false)
+
+ const columnGroupSelectionDropdownNoPermission = await loader.getHarness(ColumnGroupSelectionHarness)
+ expect(columnGroupSelectionDropdownNoPermission).toBeTruthy()
})
it('should load CustomGroupColumnSelector', async () => {
@@ -933,18 +962,18 @@ describe('InteractiveDataViewComponent', () => {
it('should move item up in picklist active columns list', async () => {
const spy = jest.spyOn(CustomGroupColumnSelectorComponent.prototype, 'onSaveClick')
const expectedHeaders = [
- 'COLUMN_HEADER_NAME.DESCRIPTION',
'COLUMN_HEADER_NAME.NAME',
+ 'COLUMN_HEADER_NAME.DESCRIPTION',
'COLUMN_HEADER_NAME.STATUS',
'COLUMN_HEADER_NAME.RESPONSIBLE',
'Actions',
]
const expectedRowsData = [
- ['', 'some name', 'some status', 'someone responsible'],
- ['example description', 'example', 'status example', ''],
- ['', 'name 1', 'status name 1', ''],
- ['', 'name 2', 'status name 2', ''],
- ['', 'name 3', 'status name 3', ''],
+ ['some name', '', 'some status', 'someone responsible'],
+ ['example', 'example description', 'status example', ''],
+ ['name 1', '', 'status name 1', ''],
+ ['name 2', '', 'status name 2', ''],
+ ['name 3', '', 'status name 3', ''],
]
await activeColumnsList[1].selectItem()
await sourceControlsButtons[0].click()
@@ -963,17 +992,17 @@ describe('InteractiveDataViewComponent', () => {
const spy = jest.spyOn(CustomGroupColumnSelectorComponent.prototype, 'onSaveClick')
const expectedHeaders = [
'COLUMN_HEADER_NAME.NAME',
- 'COLUMN_HEADER_NAME.STATUS',
'COLUMN_HEADER_NAME.DESCRIPTION',
+ 'COLUMN_HEADER_NAME.STATUS',
'COLUMN_HEADER_NAME.RESPONSIBLE',
'Actions',
]
const expectedRowsData = [
- ['some name', 'some status', '', 'someone responsible'],
- ['example', 'status example', 'example description', ''],
- ['name 1', 'status name 1', '', ''],
- ['name 2', 'status name 2', '', ''],
- ['name 3', 'status name 3', '', ''],
+ ['some name', '', 'some status', 'someone responsible'],
+ ['example', 'example description', 'status example', ''],
+ ['name 1', '', 'status name 1', ''],
+ ['name 2', '', 'status name 2', ''],
+ ['name 3', '', 'status name 3', ''],
]
await activeColumnsList[1].selectItem()
@@ -1698,4 +1727,26 @@ describe('InteractiveDataViewComponent', () => {
})
})
})
+
+ it('should react on group selection change event emit', () => {
+ const columnsChangeSpy = jest.spyOn(component.displayedColumnsChange, 'emit')
+ const columnKeysChangeSpy = jest.spyOn(component.displayedColumnKeysChange, 'emit')
+
+ component.groupSelectionChangedSlotEmitter.emit({
+ activeColumns: [
+ {
+ id: 'first-col',
+ } as any,
+ {
+ id: 'second-col',
+ } as any,
+ ],
+ groupKey: 'my-search-config',
+ })
+
+ expect(component.displayedColumnKeys).toStrictEqual(['first-col', 'second-col'])
+ expect(component.selectedGroupKey).toBe('my-search-config')
+ expect(columnsChangeSpy).toHaveBeenCalled()
+ expect(columnKeysChangeSpy).toHaveBeenCalledWith(['first-col', 'second-col'])
+ })
})
diff --git a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts
index ee4b06e2..802f4264 100644
--- a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts
+++ b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts
@@ -11,7 +11,17 @@ import {
TemplateRef,
ViewChild,
} from '@angular/core'
-import { BehaviorSubject, Observable, ReplaySubject, combineLatest, map, startWith, timestamp } from 'rxjs'
+import {
+ BehaviorSubject,
+ Observable,
+ ReplaySubject,
+ combineLatest,
+ distinctUntilChanged,
+ map,
+ startWith,
+ timestamp,
+ withLatestFrom,
+} from 'rxjs'
import { DataAction } from '../../model/data-action'
import { DataSortDirection } from '../../model/data-sort-direction'
import { DataTableColumn } from '../../model/data-table-column.model'
@@ -26,6 +36,7 @@ import {
ColumnSelectionChangedEvent,
CustomGroupColumnSelectorComponentState,
} from '../custom-group-column-selector/custom-group-column-selector.component'
+import { SlotService } from '@onecx/angular-remote-components'
import { DataLayoutSelectionComponentState } from '../data-layout-selection/data-layout-selection.component'
import { DataListGridSortingComponentState } from '../data-list-grid-sorting/data-list-grid-sorting.component'
import { Filter, Row, Sort } from '../data-table/data-table.component'
@@ -36,6 +47,11 @@ export type InteractiveDataViewComponentState = ColumnGroupSelectionComponentSta
DataLayoutSelectionComponentState &
DataListGridSortingComponentState &
DataViewComponentState
+
+export interface ColumnGroupData {
+ activeColumns: DataTableColumn[]
+ groupKey: string
+}
@Component({
selector: 'ocx-interactive-data-view',
templateUrl: './interactive-data-view.component.html',
@@ -58,6 +74,7 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
dataListGridSortingComponentState$ = new ReplaySubject(1)
dataViewComponentState$ = new ReplaySubject(1)
+ @Input() searchConfigPermission: string | undefined
@Input() deletePermission: string | undefined
@Input() editPermission: string | undefined
@Input() viewPermission: string | undefined
@@ -102,7 +119,7 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
@Input() selectedRows: Row[] = []
displayedColumnKeys$ = new BehaviorSubject([])
displayedColumns$: Observable | undefined
- @Input()
+ @Input()
get displayedColumnKeys(): string[] {
return this.displayedColumnKeys$.getValue()
}
@@ -114,11 +131,11 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
*/
@Input()
get displayedColumns(): DataTableColumn[] {
- return (
- (this.displayedColumnKeys
- .map((d) => this.columns.find((c) => c.id === d))
- .filter((d) => d) as DataTableColumn[]) ?? []
- );
+ return (
+ (this.displayedColumnKeys
+ .map((d) => this.columns.find((c) => c.id === d))
+ .filter((d) => d) as DataTableColumn[]) ?? []
+ )
}
set displayedColumns(value: DataTableColumn[]) {
this.displayedColumnKeys$.next(value.map((d) => d.id))
@@ -206,7 +223,13 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
@Output() componentStateChanged = new EventEmitter()
- selectedGroupKey = ''
+ selectedGroupKey$ = new BehaviorSubject('')
+ get selectedGroupKey(): string | undefined {
+ return this.selectedGroupKey$.getValue()
+ }
+ set selectedGroupKey(value: string | undefined) {
+ this.selectedGroupKey$.next(value)
+ }
isDeleteItemObserved: boolean | undefined
isViewItemObserved: boolean | undefined
isEditItemObserved: boolean | undefined
@@ -315,22 +338,66 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
this._data = value
}
+ columnGroupSlotName = 'onecx-shell-column-group-selection'
+ isColumnGroupSelectionComponentDefined$: Observable
+ groupSelectionChangedSlotEmitter = new EventEmitter()
+
+ constructor(private slotService: SlotService) {
+ this.isColumnGroupSelectionComponentDefined$ = this.slotService
+ .isSomeComponentDefinedForSlot(this.columnGroupSlotName)
+ .pipe(startWith(true))
+
+ this.groupSelectionChangedSlotEmitter.subscribe((event: ColumnGroupData | undefined) => {
+ if (event === undefined) {
+ event = {
+ activeColumns: this.displayedColumns,
+ groupKey: this.selectedGroupKey ?? this.defaultGroupKey,
+ }
+ }
+ this.displayedColumnKeys$.next(event.activeColumns.map((col) => col.id))
+ this.selectedGroupKey$.next(event.groupKey)
+ this.displayedColumnsChange.emit(this.displayedColumns)
+ this.displayedColumnKeysChange.emit(this.displayedColumnKeys)
+ this.columnGroupSelectionComponentState$.next({
+ activeColumnGroupKey: event.groupKey,
+ displayedColumns: event.activeColumns,
+ })
+ })
+
+ this.dataViewLayoutChange
+ .pipe(withLatestFrom(this.isColumnGroupSelectionComponentDefined$))
+ .subscribe(([_, columnGroupComponentDefined]) => {
+ if (columnGroupComponentDefined) {
+ if (
+ !(
+ this.columns.find((c) => c.nameKey === this.selectedGroupKey) ||
+ this.selectedGroupKey === this.customGroupKey
+ )
+ ) {
+ this.selectedGroupKey$.next(undefined)
+ }
+ }
+ })
+ }
+
ngOnInit(): void {
this.selectedGroupKey = this.defaultGroupKey
- if(!this.displayedColumns || this.displayedColumns.length === 0) {
+ if (!this.displayedColumns || this.displayedColumns.length === 0) {
this.displayedColumnKeys = this.columns.map((column) => column.id)
}
if (this.defaultGroupKey) {
- this.displayedColumnKeys = this.columns.filter((column) =>
- column.predefinedGroupKeys?.includes(this.defaultGroupKey)
- ).map((column) => column.id)
+ this.displayedColumnKeys = this.columns
+ .filter((column) => column.predefinedGroupKeys?.includes(this.defaultGroupKey))
+ .map((column) => column.id)
}
- this.displayedColumns$ = this.displayedColumnKeys$.pipe(map((columnKeys) => (
- (columnKeys
- .map((key) => this.columns.find((col) => col.id === key))
- .filter((d) => d) as DataTableColumn[]) ?? []
- )))
- // TODO: Remove following line once displayedColumns (deprecated) has been removed
+ this.displayedColumns$ = this.displayedColumnKeys$.pipe(
+ distinctUntilChanged((prev, curr) => prev.length === curr.length && prev.every((v) => curr.includes(v))),
+ map(
+ (columnKeys) =>
+ (columnKeys.map((key) => this.columns.find((col) => col.id === key)).filter((d) => d) as DataTableColumn[]) ??
+ []
+ )
+ )
this.displayedColumnsChange.emit(this.displayedColumns)
this.displayedColumnKeysChange.emit(this.displayedColumnKeys)
if (!this.groupSelectionNoGroupSelectedKey) {
@@ -340,10 +407,11 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
let dataListGridSortingComponentState$: Observable> =
this.dataListGridSortingComponentState$
- let columnGroupSelectionComponentState$: Observable> =
+ let columnGroupSelectionComponentState$: Observable> =
this.columnGroupSelectionComponentState$
- let customGroupColumnSelectorComponentState$: Observable> =
- this.customGroupColumnSelectorComponentState$
+ let customGroupColumnSelectorComponentState$: Observable<
+ CustomGroupColumnSelectorComponentState | Record
+ > = this.customGroupColumnSelectorComponentState$
if (this.layout === 'table') {
dataListGridSortingComponentState$ = dataListGridSortingComponentState$.pipe(startWith({}))
@@ -577,5 +645,4 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
this.pageSize = event
this.pageSizeChanged.emit(event)
}
-
}
diff --git a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.html b/libs/angular-accelerator/src/lib/components/search-config/search-config.component.html
deleted file mode 100644
index 767142e7..00000000
--- a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
diff --git a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.scss b/libs/angular-accelerator/src/lib/components/search-config/search-config.component.scss
deleted file mode 100644
index e69de29b..00000000
diff --git a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.spec.ts b/libs/angular-accelerator/src/lib/components/search-config/search-config.component.spec.ts
deleted file mode 100644
index 6b3a4022..00000000
--- a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.spec.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
-import { ReactiveFormsModule } from '@angular/forms'
-import { NoopAnimationsModule } from '@angular/platform-browser/animations'
-import { provideHttpClientTesting } from '@angular/common/http/testing'
-import { TranslateService } from '@ngx-translate/core'
-import { TranslateTestingModule } from 'ngx-translate-testing'
-import { MessageModule } from 'primeng/message'
-import { AngularAcceleratorPrimeNgModule } from '../../angular-accelerator-primeng.module'
-import { SearchConfigComponent } from './search-config.component'
-import { SearchConfigHarness } from '../../../../testing'
-import { SearchConfigInfo } from '../../model/search-config-info'
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('SearchConfigComponent', () => {
- let translateService: TranslateService
- let component: SearchConfigComponent
- let fixture: ComponentFixture
-
- const searchConfigs: SearchConfigInfo[] = [
- {
- id: '01',
- name: 'Simple search config',
- },
- {
- id: '02',
- name: 'Adapted search config',
- },
- ]
-
- const emptySearchConfigEntry: SearchConfigInfo[] = []
-
- const placeholderKey = 'OCX_SEARCH_HEADER.OCX_SEARCH_CONFIG.DROPDOWN_DEFAULT'
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [SearchConfigComponent],
- imports: [NoopAnimationsModule,
- MessageModule,
- TranslateTestingModule.withTranslations({
- en: require('./../../../../assets/i18n/en.json'),
- de: require('./../../../../assets/i18n/de.json'),
- }),
- AngularAcceleratorPrimeNgModule,
- ReactiveFormsModule],
- providers: [
- {
- useValue: {
- baseHref: '/base/path',
- mountPath: '/base/path',
- remoteBaseUrl: 'http://localhost:4200',
- shellName: 'shell',
- },
- },
- provideHttpClient(withInterceptorsFromDi()),
- provideHttpClientTesting(),
- ]
-}).compileComponents()
-
- fixture = TestBed.createComponent(SearchConfigComponent)
- component = fixture.componentInstance
- component.searchConfigs = searchConfigs
- translateService = TestBed.inject(TranslateService)
- translateService.setDefaultLang('en')
- translateService.use('en')
- fixture.detectChanges()
- })
-
- it('should create', () => {
- expect(component).toBeTruthy()
- })
-
- it('should load the PDropdownHarness', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- expect(dropdown).toBeTruthy()
- })
-
- it('should open the dropdown', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- await dropdown?.open()
- expect(await dropdown?.isOpen()).toBeTruthy()
- })
-
- it('should display a dropdown with a hard coded search config', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- const items = await dropdown?.getDropdownItems()
- expect(items?.length).toEqual(searchConfigs.length)
- })
-
- it('should display no dropdown if the search config is empty', async () => {
- component.searchConfigs = emptySearchConfigEntry
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- expect(dropdown).toBeFalsy()
- })
-
- it('should display the values in the fields after selecting the fist hard coded search config', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- const selectedDropdownItem = await dropdown?.selectedDropdownItemText(0)
- expect(selectedDropdownItem).toEqual(searchConfigs[0].name)
- })
-
- it('should display the values in the fields after selecting the fist hard coded search config', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- const selectedDropdownItem = await dropdown?.selectedDropdownItemText(1)
- expect(selectedDropdownItem).toEqual(searchConfigs[1].name)
- })
-
- it('should display the values in the fields correctly after selecting the fist search config and then selecting the second search config', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- let selectedDropdownItem = await dropdown?.selectedDropdownItemText(0)
- selectedDropdownItem = await dropdown?.selectedDropdownItemText(1)
- expect(selectedDropdownItem).toEqual(searchConfigs[1].name)
- })
-
- it('should have the option to remove the selection', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- expect(await dropdown?.hasClearOption()).toBeTruthy()
- })
-
- it('should display the right default message', async () => {
- const searchConfigHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, SearchConfigHarness)
- const dropdown = await searchConfigHarness.getSearchConfigDropdown()
- const definedDefaultKeyTranslation = translateService.instant(placeholderKey)
- expect(await dropdown?.getDefaultText()).toEqual(definedDefaultKeyTranslation)
- })
-})
diff --git a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.ts b/libs/angular-accelerator/src/lib/components/search-config/search-config.component.ts
deleted file mode 100644
index d5ca6ff5..00000000
--- a/libs/angular-accelerator/src/lib/components/search-config/search-config.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
-import { FormControl, FormGroup } from '@angular/forms'
-import { SearchConfigInfo } from '../../model/search-config-info'
-
-@Component({
- selector: 'ocx-search-config',
- templateUrl: './search-config.component.html',
- styleUrls: ['./search-config.component.scss'],
-})
-export class SearchConfigComponent implements OnInit {
- @Input()
- searchConfigs: SearchConfigInfo[] | [] | undefined
-
- @Input() placeholderKey = 'OCX_SEARCH_HEADER.OCX_SEARCH_CONFIG.DROPDOWN_DEFAULT'
-
- @Output()
- selectedSearchConfigChanged: EventEmitter = new EventEmitter()
-
- formGroup: FormGroup | undefined
- ngOnInit(): void {
- this.formGroup = new FormGroup({
- searchConfigForm: new FormControl(null),
- })
- }
-
- onSearchConfigChange(event: { value: SearchConfigInfo }) {
- this.selectedSearchConfigChanged?.emit(event.value)
- }
-}
diff --git a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.html b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.html
index fb6026a9..5183eb11 100644
--- a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.html
+++ b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.html
@@ -5,8 +5,20 @@
[actions]="headerActions"
>
-
-
+
+
+
+
+
+
+
diff --git a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.spec.ts b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.spec.ts
index 3cd940a9..36d2acda 100644
--- a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.spec.ts
+++ b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.spec.ts
@@ -4,21 +4,26 @@ import { provideHttpClientTesting } from '@angular/common/http/testing'
import { TranslateTestingModule } from 'ngx-translate-testing'
import { ButtonModule } from 'primeng/button'
import { BreadcrumbModule } from 'primeng/breadcrumb'
-import { AppStateService } from '@onecx/angular-integration-interface'
+import { AppStateService, UserService } from '@onecx/angular-integration-interface'
import { AngularAcceleratorModule } from '../../angular-accelerator.module'
import { SearchHeaderComponent } from './search-header.component'
import { PageHeaderComponent } from '../page-header/page-header.component'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { AppStateServiceMock, provideAppStateServiceMock } from '@onecx/angular-integration-interface/mocks'
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
+import { HarnessLoader } from '@angular/cdk/testing'
+import { SlotHarness } from '@onecx/angular-accelerator/testing'
+import { IfPermissionDirective } from '../../directives/if-permission.directive'
describe('SearchHeaderComponent', () => {
let mockAppStateService: AppStateServiceMock
let component: SearchHeaderComponent
let fixture: ComponentFixture
+ let loader: HarnessLoader
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [SearchHeaderComponent, PageHeaderComponent],
+ declarations: [SearchHeaderComponent, PageHeaderComponent, IfPermissionDirective],
imports: [
TranslateTestingModule.withTranslations({}),
RouterTestingModule,
@@ -46,9 +51,30 @@ describe('SearchHeaderComponent', () => {
fixture = TestBed.createComponent(SearchHeaderComponent)
component = fixture.componentInstance
fixture.detectChanges()
+
+ loader = TestbedHarnessEnvironment.loader(fixture)
})
it('should create', () => {
expect(component).toBeTruthy()
})
+
+ it('should not display search config slot if search config change is not observed', async () => {
+ const slot = await loader.getHarnessOrNull(SlotHarness)
+ expect(slot).toBeFalsy()
+ })
+
+ it('should display search config slot if search config change is observed, pageName is defined and permission is met', async () => {
+ const userService = TestBed.inject(UserService)
+ jest.spyOn(userService, 'hasPermission').mockReturnValue(true)
+ const sub = component.selectedSearchConfigChanged.subscribe()
+ component.pageName = 'myPageName'
+ component.searchConfigPermission = 'PRODUCT#USE_SEARCHCONFIGS'
+ fixture.detectChanges()
+
+ const slot = await loader.getHarness(SlotHarness)
+ expect(slot).toBeTruthy()
+
+ sub.unsubscribe()
+ })
})
diff --git a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.ts b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.ts
index 55538fdb..f5da7ae6 100644
--- a/libs/angular-accelerator/src/lib/components/search-header/search-header.component.ts
+++ b/libs/angular-accelerator/src/lib/components/search-header/search-header.component.ts
@@ -1,20 +1,32 @@
import {
+ AfterContentInit,
AfterViewInit,
Component,
ContentChild,
+ ContentChildren,
ElementRef,
EventEmitter,
Input,
Output,
+ QueryList,
TemplateRef,
ViewChild,
} from '@angular/core'
import { Action } from '../page-header/page-header.component'
-import { SearchConfigInfo } from '../../model/search-config-info'
+import { FormControlName, FormGroup, FormGroupDirective } from '@angular/forms'
+import { Observable, combineLatest, debounceTime, map, of, startWith } from 'rxjs'
+import { getLocation } from '@onecx/accelerator'
export interface SearchHeaderComponentState {
activeViewMode?: 'basic' | 'advanced'
- selectedSearchConfig?: SearchConfigInfo
+ selectedSearchConfig?: string | null
+}
+
+export interface SearchConfigData {
+ name: string | undefined
+ fieldValues: { [key: string]: string }
+ displayedColumnsIds: string[]
+ viewMode: 'basic' | 'advanced'
}
/**
@@ -26,9 +38,9 @@ export interface SearchHeaderComponentState {
@Component({
selector: 'ocx-search-header',
templateUrl: './search-header.component.html',
+ providers: [],
})
-export class SearchHeaderComponent implements AfterViewInit {
- @Input() searchConfigs: SearchConfigInfo[] | undefined
+export class SearchHeaderComponent implements AfterContentInit, AfterViewInit {
@Input() header = ''
/**
@@ -42,7 +54,22 @@ export class SearchHeaderComponent implements AfterViewInit {
this.header = value
}
@Input() subheader: string | undefined
- @Input() viewMode: 'basic' | 'advanced' = 'basic'
+ _viewMode: 'basic' | 'advanced' = 'basic'
+ @Input()
+ get viewMode(): 'basic' | 'advanced' {
+ return this._viewMode
+ }
+ set viewMode(viewMode: 'basic' | 'advanced') {
+ if (this.viewMode !== viewMode) {
+ this._viewMode = viewMode
+ this.viewModeChanged?.emit(this.viewMode)
+ this.componentStateChanged.emit({
+ activeViewMode: this.viewMode,
+ })
+ this.updateHeaderActions()
+ setTimeout(() => this.addKeyUpEventListener())
+ }
+ }
@Input() manualBreadcrumbs = false
_actions: Action[] = []
@Input()
@@ -53,12 +80,14 @@ export class SearchHeaderComponent implements AfterViewInit {
this._actions = value
this.updateHeaderActions()
}
+ @Input() searchConfigPermission: string | undefined
@Input() searchButtonDisabled = false
@Input() resetButtonDisabled = false
+ @Input() pageName: string | undefined = getLocation().applicationPath
@Output() searched: EventEmitter = new EventEmitter()
@Output() resetted: EventEmitter = new EventEmitter()
- @Output() selectedSearchConfigChanged: EventEmitter = new EventEmitter()
+ @Output() selectedSearchConfigChanged: EventEmitter = new EventEmitter()
@Output() viewModeChanged: EventEmitter<'basic' | 'advanced'> = new EventEmitter()
@Output() componentStateChanged: EventEmitter = new EventEmitter()
@ContentChild('additionalToolbarContent')
@@ -74,23 +103,56 @@ export class SearchHeaderComponent implements AfterViewInit {
return this.additionalToolbarContentLeft
}
+ get searchConfigChangeObserved(): boolean {
+ return this.selectedSearchConfigChanged.observed
+ }
+
+ @ContentChild(FormGroupDirective) formGroup: FormGroup | undefined
+ @ContentChildren(FormControlName, { descendants: true }) visibleFormControls!: QueryList
+
@ViewChild('searchParameterFields') searchParameterFields: ElementRef | undefined
hasAdvanced = false
headerActions: Action[] = []
+ fieldValues$: Observable<{ [key: string]: unknown }> | undefined = of({})
+ searchConfigChangedSlotEmitter: EventEmitter = new EventEmitter()
+
+ constructor() {
+ this.searchConfigChangedSlotEmitter.subscribe((config) => {
+ this.componentStateChanged.emit({
+ selectedSearchConfig: config?.name ?? null,
+ })
+ this.selectedSearchConfigChanged.emit(config)
+ })
+ }
+
+ ngAfterContentInit(): void {
+ if (this.formGroup) {
+ this.fieldValues$ = combineLatest([
+ this.formGroup.valueChanges.pipe(startWith({})),
+ this.visibleFormControls.changes.pipe(startWith(null)),
+ ]).pipe(
+ debounceTime(100),
+ map(([values, _]) =>
+ Object.entries(values ?? {}).reduce(
+ (acc, [key, value]) => ({
+ ...acc,
+ [key]: this.isVisible(key) ? value || undefined : undefined,
+ }),
+ {}
+ )
+ )
+ )
+ }
+ }
+
ngAfterViewInit(): void {
this.addKeyUpEventListener()
}
toggleViewMode() {
this.viewMode = this.viewMode === 'basic' ? 'advanced' : 'basic'
- this.viewModeChanged?.emit(this.viewMode)
- this.componentStateChanged.emit({
- activeViewMode: this.viewMode
- })
- this.updateHeaderActions()
- setTimeout(() => this.addKeyUpEventListener())
}
onResetClicked() {
@@ -137,10 +199,9 @@ export class SearchHeaderComponent implements AfterViewInit {
}
}
- confirmSearchConfig(searchConfig: SearchConfigInfo) {
- this.selectedSearchConfigChanged?.emit(searchConfig)
- this.componentStateChanged.emit({
- selectedSearchConfig: searchConfig
- })
+ private isVisible(control: string) {
+ return this.visibleFormControls.some(
+ (formControl) => formControl.name !== null && String(formControl.name) === control
+ )
}
}
diff --git a/libs/angular-accelerator/src/lib/components/search-header/search-header.stories.ts b/libs/angular-accelerator/src/lib/components/search-header/search-header.stories.ts
index d59c3a99..35e81df6 100644
--- a/libs/angular-accelerator/src/lib/components/search-header/search-header.stories.ts
+++ b/libs/angular-accelerator/src/lib/components/search-header/search-header.stories.ts
@@ -13,7 +13,6 @@ import { SkeletonModule } from 'primeng/skeleton'
import { DynamicPipe } from '../../pipes/dynamic.pipe'
import { StorybookTranslateModule } from '../../storybook-translate.module'
import { PageHeaderComponent } from '../page-header/page-header.component'
-import { SearchConfigComponent } from '../search-config/search-config.component'
import { StorybookBreadcrumbModule } from './../../storybook-breadcrumb.module'
import { SearchHeaderComponent } from './search-header.component'
@@ -29,7 +28,7 @@ export default {
],
}),
moduleMetadata({
- declarations: [SearchHeaderComponent, DynamicPipe, PageHeaderComponent, SearchConfigComponent],
+ declarations: [SearchHeaderComponent, DynamicPipe, PageHeaderComponent],
imports: [
MenuModule,
InputTextModule,
@@ -115,11 +114,11 @@ const BasicSearchHeader: StoryFn = (args) => ({
})
export const WithCustomTemplates = {
- render: BasicSearchHeader,
- argTypes: {
- resetted: { action: 'resetted' },
- },
- args: {
- header: 'My title',
- },
- }
\ No newline at end of file
+ render: BasicSearchHeader,
+ argTypes: {
+ resetted: { action: 'resetted' },
+ },
+ args: {
+ header: 'My title',
+ },
+}
diff --git a/libs/angular-accelerator/src/lib/directives/if-permission.directive.ts b/libs/angular-accelerator/src/lib/directives/if-permission.directive.ts
index ed98fbc6..82023219 100644
--- a/libs/angular-accelerator/src/lib/directives/if-permission.directive.ts
+++ b/libs/angular-accelerator/src/lib/directives/if-permission.directive.ts
@@ -29,8 +29,8 @@ export const HAS_PERMISSION_CHECKER = new InjectionToken('
@Directive({ selector: '[ocxIfPermission], [ocxIfNotPermission]' })
export class IfPermissionDirective implements OnInit {
- @Input('ocxIfPermission') permission: string | undefined
- @Input('ocxIfNotPermission') set notPermission(value: string | undefined) {
+ @Input('ocxIfPermission') permission: string | string[] | undefined
+ @Input('ocxIfNotPermission') set notPermission(value: string | string[] | undefined) {
this.permission = value
this.negate = true
}
@@ -43,6 +43,13 @@ export class IfPermissionDirective implements OnInit {
this.ocxIfPermissionPermissions = value
}
+ @Input()
+ ocxIfPermissionElseTemplate: TemplateRef | undefined
+ @Input()
+ set ocxIfNotPermissionElseTemplate(value: TemplateRef | undefined) {
+ this.ocxIfPermissionElseTemplate = value
+ }
+
private permissionChecker: HasPermissionChecker | undefined
negate = false
@@ -65,11 +72,15 @@ export class IfPermissionDirective implements OnInit {
ngOnInit() {
if (this.permission) {
- if (this.negate === this.hasPermission(this.permission)) {
- if (this.onMissingPermission === 'disable') {
- this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'disabled')
+ if (this.negate === this.hasPermission(Array.isArray(this.permission) ? this.permission : [this.permission])) {
+ if (this.ocxIfPermissionElseTemplate) {
+ this.viewContainer.createEmbeddedView(this.ocxIfPermissionElseTemplate)
} else {
- this.viewContainer.clear()
+ if (this.onMissingPermission === 'disable') {
+ this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'disabled')
+ } else {
+ this.viewContainer.clear()
+ }
}
} else {
if (this.templateRef) {
@@ -79,14 +90,14 @@ export class IfPermissionDirective implements OnInit {
}
}
- hasPermission(permission: string) {
+ hasPermission(permission: string[]) {
if (this.ocxIfPermissionPermissions) {
- const result = this.ocxIfPermissionPermissions.includes(permission)
+ const result = permission.every((p) => this.ocxIfPermissionPermissions?.includes(p))
if (!result) {
console.log('👮♀️ No permission in overwrites for: `', permission)
}
return result
}
- return this.permissionChecker?.hasPermission(permission)
+ return permission.every((p) => this.permissionChecker?.hasPermission(p))
}
}
diff --git a/libs/angular-accelerator/src/lib/model/search-config-info.ts b/libs/angular-accelerator/src/lib/model/search-config-info.ts
deleted file mode 100644
index 693a6522..00000000
--- a/libs/angular-accelerator/src/lib/model/search-config-info.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export interface SearchConfigInfo {
- id: string
- name: string
-}
diff --git a/libs/angular-accelerator/src/lib/utils/create-remote-component-and-mfe-translate-loader.utils.ts b/libs/angular-accelerator/src/lib/utils/create-remote-component-and-mfe-translate-loader.utils.ts
new file mode 100644
index 00000000..2465e6ed
--- /dev/null
+++ b/libs/angular-accelerator/src/lib/utils/create-remote-component-and-mfe-translate-loader.utils.ts
@@ -0,0 +1,32 @@
+import { HttpClient } from '@angular/common/http'
+import { ReplaySubject, map } from 'rxjs'
+import { TranslationCacheService } from '../services/translation-cache.service'
+import { AppStateService } from '@onecx/angular-integration-interface'
+import { AsyncTranslateLoader } from './async-translate-loader.utils'
+import { TranslateCombinedLoader } from './translate.combined.loader'
+import { createRemoteComponentTranslateLoader } from './create-remote-component-translate-loader.utils'
+import { CachingTranslateLoader } from './caching-translate-loader.utils'
+import { Location } from '@angular/common'
+
+export function createRemoteComponentAndMfeTranslateLoader(
+ httpClient: HttpClient,
+ baseUrl: ReplaySubject,
+ translationCacheService: TranslationCacheService,
+ appStateService: AppStateService
+) {
+ return new AsyncTranslateLoader(
+ appStateService.currentMfe$.pipe(
+ map((currentMfe) => {
+ return new TranslateCombinedLoader(
+ createRemoteComponentTranslateLoader(httpClient, baseUrl, translationCacheService),
+ new CachingTranslateLoader(
+ translationCacheService,
+ httpClient,
+ Location.joinWithSlash(currentMfe.remoteBaseUrl, 'assets/i18n/'),
+ '.json'
+ )
+ )
+ })
+ )
+ )
+}
diff --git a/libs/angular-accelerator/testing/index.ts b/libs/angular-accelerator/testing/index.ts
index 65912d3e..03f238b6 100644
--- a/libs/angular-accelerator/testing/index.ts
+++ b/libs/angular-accelerator/testing/index.ts
@@ -11,7 +11,7 @@ export * from './group-by-count-diagram.harness'
export * from './interactive-data-view.harness'
export * from './more-actions-menu-button.harness'
export * from './page-header.harness'
-export * from './search-config.harness'
+export * from './slot.harness'
export * from './search-header.harness'
export * from '@angular/cdk/testing'
diff --git a/libs/angular-accelerator/testing/interactive-data-view.harness.ts b/libs/angular-accelerator/testing/interactive-data-view.harness.ts
index f17cce6e..af0cdff5 100644
--- a/libs/angular-accelerator/testing/interactive-data-view.harness.ts
+++ b/libs/angular-accelerator/testing/interactive-data-view.harness.ts
@@ -4,6 +4,7 @@ import { PDropdownHarness } from '@onecx/angular-testing'
import { CustomGroupColumnSelectorHarness } from '.'
import { DataLayoutSelectionHarness } from './data-layout-selection.harness'
import { DataViewHarness } from './data-view.harness'
+import { SlotHarness } from './slot.harness'
export class InteractiveDataViewHarness extends ContentContainerComponentHarness {
static hostSelector = 'ocx-interactive-data-view'
@@ -12,7 +13,8 @@ export class InteractiveDataViewHarness extends ContentContainerComponentHarness
getColumnGroupSelectionDropdown = this.locatorForOptional(
PDropdownHarness.with({ id: 'columnGroupSelectionDropdown' })
)
- getCustomGroupColumnSelector = this.locatorFor(CustomGroupColumnSelectorHarness)
+ getCustomGroupColumnSelector = this.locatorForOptional(CustomGroupColumnSelectorHarness)
+ getCustomGroupColumnSelectorSlot = this.locatorForOptional(SlotHarness)
getDataListGridSortingDropdown = this.locatorForOptional(PDropdownHarness.with({ id: 'dataListGridSortingDropdown' }))
getDataListGridSortingButton = this.locatorForOptional(PButtonHarness.with({ id: 'dataListGridSortingButton' }))
getDataView = this.locatorFor(DataViewHarness)
diff --git a/libs/angular-accelerator/testing/search-config.harness.ts b/libs/angular-accelerator/testing/search-config.harness.ts
deleted file mode 100644
index 1820f6cd..00000000
--- a/libs/angular-accelerator/testing/search-config.harness.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { ContentContainerComponentHarness } from '@angular/cdk/testing'
-import { PDropdownHarness } from '@onecx/angular-testing'
-
-export class SearchConfigHarness extends ContentContainerComponentHarness {
- static hostSelector = 'ocx-search-config'
-
- getSearchConfigDropdown = this.locatorForOptional(PDropdownHarness.with({ id: 'searchConfig' }))
-}
diff --git a/libs/angular-accelerator/testing/search-header.harness.ts b/libs/angular-accelerator/testing/search-header.harness.ts
index 941634ad..90986e08 100644
--- a/libs/angular-accelerator/testing/search-header.harness.ts
+++ b/libs/angular-accelerator/testing/search-header.harness.ts
@@ -1,14 +1,12 @@
import { ComponentHarness } from '@angular/cdk/testing'
import { PButtonHarness } from '@onecx/angular-testing'
import { PageHeaderHarness } from './page-header.harness'
-import { SearchConfigHarness } from './search-config.harness'
import { MoreActionsMenuButtonHarness } from './more-actions-menu-button.harness'
export class SearchHeaderHarness extends ComponentHarness {
static hostSelector = 'ocx-search-header'
getPageHeader = this.locatorFor(PageHeaderHarness)
- getSearchConfig = this.locatorFor(SearchConfigHarness)
getSearchButton = this.locatorFor(
PButtonHarness.with({
id: 'searchButton',
diff --git a/libs/angular-accelerator/testing/slot.harness.ts b/libs/angular-accelerator/testing/slot.harness.ts
new file mode 100644
index 00000000..f6083f4c
--- /dev/null
+++ b/libs/angular-accelerator/testing/slot.harness.ts
@@ -0,0 +1,9 @@
+import { BaseHarnessFilters, ContentContainerComponentHarness } from '@angular/cdk/testing'
+
+export interface SlotHarnessFilters extends BaseHarnessFilters {
+ name?: string
+}
+
+export class SlotHarness extends ContentContainerComponentHarness {
+ static hostSelector = 'ocx-slot'
+}
diff --git a/libs/angular-integration-interface/mocks/index.ts b/libs/angular-integration-interface/mocks/index.ts
index 1f89a426..8b444f7b 100644
--- a/libs/angular-integration-interface/mocks/index.ts
+++ b/libs/angular-integration-interface/mocks/index.ts
@@ -1,2 +1,3 @@
export * from './mock-user-service'
export * from './app-state-service-mock'
+export * from './fake-topic'
diff --git a/libs/angular-integration-interface/src/index.ts b/libs/angular-integration-interface/src/index.ts
index ffd738f4..942aee7c 100644
--- a/libs/angular-integration-interface/src/index.ts
+++ b/libs/angular-integration-interface/src/index.ts
@@ -1,4 +1,5 @@
// services
+export * from './lib/services/app-config-service'
export * from './lib/services/app-state.service'
export * from './lib/services/configuration.service'
export * from './lib/services/user.service'
diff --git a/libs/angular-accelerator/src/lib/services/app-config-service.ts b/libs/angular-integration-interface/src/lib/services/app-config-service.ts
similarity index 100%
rename from libs/angular-accelerator/src/lib/services/app-config-service.ts
rename to libs/angular-integration-interface/src/lib/services/app-config-service.ts
diff --git a/libs/angular-remote-components/mocks/index.ts b/libs/angular-remote-components/mocks/index.ts
new file mode 100644
index 00000000..085a3d9f
--- /dev/null
+++ b/libs/angular-remote-components/mocks/index.ts
@@ -0,0 +1 @@
+export * from './slot-service-mock'
diff --git a/libs/angular-remote-components/mocks/ng-package.json b/libs/angular-remote-components/mocks/ng-package.json
new file mode 100644
index 00000000..ecef3ed8
--- /dev/null
+++ b/libs/angular-remote-components/mocks/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "index.ts"
+ }
+}
\ No newline at end of file
diff --git a/libs/angular-remote-components/mocks/slot-service-mock.ts b/libs/angular-remote-components/mocks/slot-service-mock.ts
new file mode 100644
index 00000000..a64dfa4e
--- /dev/null
+++ b/libs/angular-remote-components/mocks/slot-service-mock.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@angular/core'
+import { BehaviorSubject, Observable, map } from 'rxjs'
+
+@Injectable()
+export class SlotServiceMock {
+ _componentsDefinedForSlot: BehaviorSubject<{
+ [slot_key: string]: string[]
+ }> = new BehaviorSubject({})
+ isSomeComponentDefinedForSlot(slotName: string): Observable {
+ return this._componentsDefinedForSlot.pipe(
+ map((assignments) => {
+ return slotName in assignments && assignments[slotName].length > 0
+ })
+ )
+ }
+
+ getComponentsForSlot(slotName: string) {
+ return this._componentsDefinedForSlot.pipe(
+ map((assignments) => {
+ return Object.keys(assignments).includes(slotName) ? assignments[slotName] : []
+ })
+ )
+ }
+
+ assignComponentToSlot(componentName: string, slotName: string) {
+ const currentAssignments = this._componentsDefinedForSlot.getValue()
+ this._componentsDefinedForSlot.next({
+ ...currentAssignments,
+ [slotName]: slotName in currentAssignments ? currentAssignments[slotName].concat(componentName) : [componentName],
+ })
+ }
+}
diff --git a/libs/angular-remote-components/package.json b/libs/angular-remote-components/package.json
index 9c5c2d0f..71fe4191 100644
--- a/libs/angular-remote-components/package.json
+++ b/libs/angular-remote-components/package.json
@@ -5,7 +5,6 @@
"peerDependencies": {
"@angular/common": "^18.0.5",
"@angular/core": "^18.0.5",
- "@onecx/angular-accelerator": "^5",
"@onecx/integration-interface": "^5",
"@ngx-translate/core": "^15.0.0",
"@angular-architects/module-federation": "^18.0.4",
diff --git a/libs/angular-remote-components/src/lib/angular-remote-components.module.ts b/libs/angular-remote-components/src/lib/angular-remote-components.module.ts
index 43a2f528..5f03ca09 100644
--- a/libs/angular-remote-components/src/lib/angular-remote-components.module.ts
+++ b/libs/angular-remote-components/src/lib/angular-remote-components.module.ts
@@ -1,12 +1,17 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { SlotComponent } from './components/slot/slot.component'
-import { AppConfigService } from '@onecx/angular-accelerator'
+import { SLOT_SERVICE, SlotService } from './services/slot.service'
@NgModule({
imports: [CommonModule],
declarations: [SlotComponent],
exports: [SlotComponent],
- providers: [AppConfigService],
+ providers: [
+ {
+ provide: SLOT_SERVICE,
+ useExisting: SlotService,
+ },
+ ],
})
export class AngularRemoteComponentsModule {}
diff --git a/libs/angular-remote-components/src/lib/components/slot/slot.component.ts b/libs/angular-remote-components/src/lib/components/slot/slot.component.ts
index 0fd370b1..5dd7378d 100644
--- a/libs/angular-remote-components/src/lib/components/slot/slot.component.ts
+++ b/libs/angular-remote-components/src/lib/components/slot/slot.component.ts
@@ -7,6 +7,7 @@ import {
Input,
OnDestroy,
OnInit,
+ Optional,
QueryList,
TemplateRef,
Type,
@@ -118,8 +119,6 @@ export class SlotComponent implements OnInit, OnDestroy {
})
}
- updateDataSub: Subscription | undefined
-
_viewContainers$ = new BehaviorSubject | undefined>(undefined)
@ViewChildren('slot', { read: ViewContainerRef })
set viewContainers(value: QueryList) {
@@ -131,9 +130,13 @@ export class SlotComponent implements OnInit, OnDestroy {
subscription: Subscription | undefined
components$: Observable | undefined
- constructor(@Inject(SLOT_SERVICE) private slotService: SlotService) {}
+ constructor(@Optional() @Inject(SLOT_SERVICE) private slotService?: SlotService) {}
ngOnInit(): void {
+ if (!this.slotService) {
+ console.error(`SLOT_SERVICE token was not provided. ${this.name} slot will not be filled with data.`)
+ return
+ }
this.components$ = this.slotService.getComponentsForSlot(this.name)
combineLatest([this._assignedComponents$, this._inputs$, this._outputs$]).subscribe(
([components, inputs, outputs]) => {
diff --git a/libs/angular-remote-components/src/lib/services/slot.service.ts b/libs/angular-remote-components/src/lib/services/slot.service.ts
index 4e1b84da..0ef5abb4 100644
--- a/libs/angular-remote-components/src/lib/services/slot.service.ts
+++ b/libs/angular-remote-components/src/lib/services/slot.service.ts
@@ -59,7 +59,11 @@ export class SlotService implements SlotServiceInterface {
isSomeComponentDefinedForSlot(slotName: string): Observable {
return this.remoteComponents$.pipe(
- map((remoteComponentsInfo) => remoteComponentsInfo.slots.some((slotMapping) => slotMapping.name === slotName))
+ map((remoteComponentsInfo) =>
+ remoteComponentsInfo.slots.some(
+ (slotMapping) => slotMapping.name === slotName && slotMapping.components.length > 0
+ )
+ )
)
}
diff --git a/libs/angular-testing/src/lib/harnesses/primeng/p-dropdown.harness.ts b/libs/angular-testing/src/lib/harnesses/primeng/p-dropdown.harness.ts
index f6ca0a26..bfd9734a 100644
--- a/libs/angular-testing/src/lib/harnesses/primeng/p-dropdown.harness.ts
+++ b/libs/angular-testing/src/lib/harnesses/primeng/p-dropdown.harness.ts
@@ -21,6 +21,10 @@ export class PDropdownHarness extends ContentContainerComponentHarness {
return await (await this.host()).getAttribute('inputId')
}
+ async getAriaLabel(): Promise {
+ return (await this.locatorForOptional('span.p-placeholder')())?.getAttribute('aria-label')
+ }
+
async getId(): Promise {
return await (await this.host()).getAttribute('id')
}
@@ -29,6 +33,10 @@ export class PDropdownHarness extends ContentContainerComponentHarness {
return (await this.locatorForOptional('span.p-placeholder')())?.text()
}
+ async getSelectedText() {
+ return (await this.locatorForOptional('span.p-dropdown-label')())?.text()
+ }
+
async isOpen(): Promise {
return (await this.locatorFor('div')()).hasClass('p-dropdown-open')
}
diff --git a/libs/portal-integration-angular/src/index.ts b/libs/portal-integration-angular/src/index.ts
index 320d9c0c..c8c09b7c 100644
--- a/libs/portal-integration-angular/src/index.ts
+++ b/libs/portal-integration-angular/src/index.ts
@@ -39,7 +39,6 @@ export * from './lib/core/components/button-dialog/dialog-message-content/dialog
export * from './lib/core/components/loading-indicator/loading-indicator.component'
export * from './lib/core/components/content-container/content-container.component'
export * from './lib/core/components/content/content.component'
-export * from './lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component'
export * from './lib/core/components/lifecycle/lifecycle.component'
// services
@@ -97,5 +96,5 @@ export {
APP_CONFIG,
AUTH_SERVICE,
SANITY_CHECK,
- APPLICATION_NAME
+ APPLICATION_NAME,
} from '@onecx/angular-integration-interface'
diff --git a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.html b/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.html
deleted file mode 100644
index 182dc5f7..00000000
--- a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
diff --git a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.scss b/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.scss
deleted file mode 100644
index a530b42b..00000000
--- a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-.searchConfigDialog {
- display: flex;
- flex-direction: column;
- gap: 1em;
- margin-bottom: 1em;
-}
-
-:host ::ng-deep .p-inputtext {
- width: 100%;
-}
-
-:host ::ng-deep .p-checkbox {
- margin-right: 1em;
-}
diff --git a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.spec.ts b/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.spec.ts
deleted file mode 100644
index 90e4937b..00000000
--- a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.spec.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { CreateOrEditSearchConfigDialogComponent } from './create-or-edit-search-config-dialog.component'
-import { CheckboxModule } from 'primeng/checkbox'
-import { MockAuthModule } from '../../../mock-auth/mock-auth.module'
-import { TranslateTestingModule } from 'ngx-translate-testing'
-import { provideHttpClientTesting } from '@angular/common/http/testing'
-import { TranslateService } from '@ngx-translate/core'
-import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
-import { CreateOrEditSearchConfigDialogHarness } from '../../../../../testing'
-import { PCheckboxHarness } from '@onecx/angular-testing'
-import { DialogState } from '../../../services/portal-dialog.service'
-import { ReactiveFormsModule } from '@angular/forms'
-import { InputTextModule } from 'primeng/inputtext'
-import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
-
-describe('CreateOrEditSearchConfigDialogComponent', () => {
- let component: CreateOrEditSearchConfigDialogComponent
- let fixture: ComponentFixture
- let translateService: TranslateService
- let dialogHarness: CreateOrEditSearchConfigDialogHarness
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [CreateOrEditSearchConfigDialogComponent],
- imports: [CheckboxModule,
- MockAuthModule,
- TranslateTestingModule.withTranslations({
- en: require('./../../../../../assets/i18n/en.json'),
- de: require('./../../../../../assets/i18n/de.json'),
- }),
- ReactiveFormsModule,
- InputTextModule],
- providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
-}).compileComponents()
-
- fixture = TestBed.createComponent(CreateOrEditSearchConfigDialogComponent)
- component = fixture.componentInstance
-
- translateService = TestBed.inject(TranslateService)
- translateService.setDefaultLang('en')
- translateService.use('en')
-
- fixture.detectChanges()
- dialogHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, CreateOrEditSearchConfigDialogHarness)
- })
-
- it('should create the component', () => {
- expect(component).toBeTruthy()
- })
-
- it('should load the CreateOrEditSearchConfigDialogHarness', async () => {
- expect(dialogHarness).toBeTruthy()
- })
-
- it('should set the DialogResult of the saveInputValuesId checkbox to true when the saveInputValuesId checkbox is checked', async () => {
- const saveInputValuesCheckbox = await dialogHarness.getHarness(
- PCheckboxHarness.with({ inputid: 'saveInputValuesId' })
- )
- await saveInputValuesCheckbox.click()
- const _state: DialogState = { button: 'primary', result: undefined }
- component.ocxDialogButtonClicked(_state)
- const dialogResult = {
- searchConfigName: '',
- saveInputValues: true,
- saveColumns: false,
- }
- expect(component.dialogResult).toEqual(dialogResult)
- })
-
- it('should set the DialogResult of the saveColumnsId checkbox initially false', async () => {
- const _state: DialogState = { button: 'primary', result: undefined }
- await component.ocxDialogButtonClicked(_state)
- const dialogResult = {
- searchConfigName: '',
- saveInputValues: false,
- saveColumns: false,
- }
- expect(component.dialogResult).toEqual(dialogResult)
- })
-
- it('should set the DialogResult of the searchConfig input Field to the entered value', async () => {
- await (await dialogHarness.getSearchConfigInputHarness()).setValue('search Config')
- const _state: DialogState = { button: 'primary', result: undefined }
- await component.ocxDialogButtonClicked(_state)
- const dialogResult = {
- searchConfigName: 'search Config',
- saveInputValues: false,
- saveColumns: false,
- }
- expect(component.dialogResult).toEqual(dialogResult)
- })
-
- it('should set the saveColumnsId checkbox initially to unchecked', async () => {
- const saveInputValuesCheckbox = await dialogHarness.getSaveColumnsCheckboxHarness()
- const checked = await saveInputValuesCheckbox.isChecked()
- expect(checked).toBeFalsy()
- })
-
- it('should set the saveInputValues checkbox initially to unchecked', async () => {
- const saveInputValuesCheckbox = await dialogHarness.getSaveInputValuesCheckboxHarness()
- const checked = await saveInputValuesCheckbox.isChecked()
- expect(checked).toBeFalsy()
- })
-
- it('should set the saveInputValues checkbox to true when it is clicked', async () => {
- const saveInputValuesCheckbox = await dialogHarness.getSaveInputValuesCheckboxHarness()
- await saveInputValuesCheckbox.click()
- const checked = await saveInputValuesCheckbox.isChecked()
- expect(checked).toBeTruthy()
- })
-
- it('should emit true when the searchConfig name is not an empty string and the saveColumnsCheckBox is clicked', async () => {
- let done: () => void
- const finished = new Promise((resolve) => (done = resolve))
- let enabled = false
- component.primaryButtonEnabled.subscribe((v) => {
- enabled = v
- done()
- })
-
- const searchConfigInputHarness = await dialogHarness.getSearchConfigInputHarness()
- searchConfigInputHarness.setValue('test')
- const saveInputValuesCheckbox = await dialogHarness.getSaveColumnsCheckboxHarness()
- await saveInputValuesCheckbox.click()
-
- await finished
- expect(enabled).toEqual(true)
- })
-
- it('emit true when the searchConfig Name is not an empty string and the saveInputValuesCheckbox is clicked', async () => {
- let done: () => void
- const finished = new Promise((resolve) => (done = resolve))
- let enabled = false
- component.primaryButtonEnabled.subscribe((v) => {
- enabled = v
- done()
- })
-
- const searchConfigInputHarness = await dialogHarness.getSearchConfigInputHarness()
- searchConfigInputHarness.setValue('test')
- const saveInputValuesCheckbox = await dialogHarness.getSaveInputValuesCheckboxHarness()
- await saveInputValuesCheckbox.click()
-
- await finished
- expect(enabled).toEqual(true)
- })
-})
diff --git a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.ts b/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.ts
deleted file mode 100644
index 3ad73901..00000000
--- a/libs/portal-integration-angular/src/lib/core/components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Component, EventEmitter, Input, Output } from '@angular/core'
-import { FormControl, FormGroup } from '@angular/forms'
-import {
- DialogButtonClicked,
- DialogPrimaryButtonDisabled,
- DialogResult,
- DialogState,
-} from '../../../services/portal-dialog.service'
-import { Observable, map } from 'rxjs'
-
-export type CreateOrEditSearchDialogContent = {
- searchConfigName: string
- saveInputValues: boolean
- saveColumns: boolean
-}
-@Component({
- selector: 'ocx-create-or-edit-search-config-dialog',
- templateUrl: './create-or-edit-search-config-dialog.component.html',
- styleUrls: ['./create-or-edit-search-config-dialog.component.scss'],
-})
-export class CreateOrEditSearchConfigDialogComponent
- implements
- DialogPrimaryButtonDisabled,
- DialogResult,
- DialogButtonClicked
-{
- @Input()
- set searchConfigName(value: string | undefined){
- this.searchConfigFormGroup.controls['searchConfigName'].setValue(value)
- }
- get searchConfigName(): string | undefined {
- return this.searchConfigFormGroup.controls['searchConfigName'].value
- }
-
- @Input()
- set saveInputValues(value: boolean | undefined) {
- this.searchConfigFormGroup.controls['saveInputValues'].setValue(value)
- }
- get saveInputValues(): boolean | undefined {
- return this.searchConfigFormGroup.controls['saveInputValues'].value
- }
-
- @Input()
- set saveColumns(value: boolean | undefined) {
- this.searchConfigFormGroup.controls['saveColumns'].setValue(value)
- }
- get saveColumns(): boolean | undefined {
- return this.searchConfigFormGroup.controls['saveColumns'].value
- }
-
- @Output() primaryButtonEnabled: EventEmitter = new EventEmitter()
-
- searchConfigFormGroup: FormGroup = new FormGroup({
- searchConfigName: new FormControl(''),
- saveInputValues: new FormControl(false),
- saveColumns: new FormControl(false),
- })
- placeHolderKey = 'OCX_SEARCH_CONFIG.PLACEHOLDER'
- dialogResult: CreateOrEditSearchDialogContent = { searchConfigName: '', saveInputValues: false, saveColumns: false }
-
- constructor() {
- this.searchConfigFormGroup.valueChanges
- .pipe(
- map(
- (dialogFormValues: CreateOrEditSearchDialogContent) =>
- !!dialogFormValues.searchConfigName && (dialogFormValues.saveInputValues || dialogFormValues.saveColumns)
- )
- )
- .subscribe(this.primaryButtonEnabled)
- }
-
- ocxDialogButtonClicked(
- _state: DialogState
- ): boolean | Observable | Promise | undefined {
- this.dialogResult = {
- searchConfigName: this.searchConfigFormGroup?.get('searchConfigName')?.value,
- saveInputValues: this.searchConfigFormGroup?.get('saveInputValues')?.value,
- saveColumns: this.searchConfigFormGroup?.get('saveColumns')?.value,
- }
- return true
- }
-}
diff --git a/libs/portal-integration-angular/src/lib/core/portal-core.module.ts b/libs/portal-integration-angular/src/lib/core/portal-core.module.ts
index e816ec4d..9b71e850 100644
--- a/libs/portal-integration-angular/src/lib/core/portal-core.module.ts
+++ b/libs/portal-integration-angular/src/lib/core/portal-core.module.ts
@@ -22,7 +22,6 @@ import { ButtonDialogComponent } from './components/button-dialog/button-dialog.
import { DialogMessageContentComponent } from './components/button-dialog/dialog-message-content/dialog-message-content.component'
import { OcxContentContainerComponent } from './components/content-container/content-container.component'
import { OcxContentComponent } from './components/content/content.component'
-import { CreateOrEditSearchConfigDialogComponent } from './components/create-or-edit-search-config-dialog/create-or-edit-search-config-dialog.component'
import { ColumnTogglerComponent } from './components/data-view-controls/column-toggler-component/column-toggler.component'
import { DataViewControlsComponent } from './components/data-view-controls/data-view-controls.component'
import { ViewTemplatePickerComponent } from './components/data-view-controls/view-template-picker/view-template-picker.component'
@@ -113,7 +112,6 @@ export class PortalMissingTranslationHandler implements MissingTranslationHandle
OcxContentContainerDirective,
OcxContentComponent,
OcxContentContainerComponent,
- CreateOrEditSearchConfigDialogComponent,
LifecycleComponent,
],
providers: [
@@ -169,7 +167,6 @@ export class PortalMissingTranslationHandler implements MissingTranslationHandle
OcxContentContainerDirective,
OcxContentComponent,
OcxContentContainerComponent,
- CreateOrEditSearchConfigDialogComponent,
LifecycleComponent,
],
})
diff --git a/libs/portal-integration-angular/testing/create-or-edit-search-config-dialog.harness.ts b/libs/portal-integration-angular/testing/create-or-edit-search-config-dialog.harness.ts
deleted file mode 100644
index 1de86db7..00000000
--- a/libs/portal-integration-angular/testing/create-or-edit-search-config-dialog.harness.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { ContentContainerComponentHarness } from '@angular/cdk/testing'
-import { PCheckboxHarness, InputHarness } from '@onecx/angular-testing'
-
-export class CreateOrEditSearchConfigDialogHarness extends ContentContainerComponentHarness {
- static hostSelector = 'ocx-create-or-edit-search-config-dialog'
-
- getSaveInputValuesCheckboxHarness() {
- return this.getHarness(PCheckboxHarness.with({ inputid: 'saveInputValuesId' }))
- }
-
- getSaveColumnsCheckboxHarness() {
- return this.getHarness(PCheckboxHarness.with({ inputid: 'saveColumnsId' }))
- }
-
- getSearchConfigInputHarness() {
- return this.getHarness(InputHarness.with({ id: 'searchConfigName' }))
- }
-}
diff --git a/libs/portal-integration-angular/testing/index.ts b/libs/portal-integration-angular/testing/index.ts
index 5fcca82e..bd7d1fdd 100644
--- a/libs/portal-integration-angular/testing/index.ts
+++ b/libs/portal-integration-angular/testing/index.ts
@@ -1,7 +1,6 @@
export * from './button-dialog.harness'
export * from './content-container.harness'
export * from './content.harness'
-export * from './create-or-edit-search-config-dialog.harness'
export * from './dialog-message-content.harness'
export * from './lifecycle.harness'
diff --git a/tsconfig.base.json b/tsconfig.base.json
index fc6d9f85..8fb61513 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -22,6 +22,7 @@
"@onecx/angular-integration-interface": ["libs/angular-integration-interface/src/index.ts"],
"@onecx/angular-integration-interface/mocks": ["libs/angular-integration-interface/mocks/index.ts"],
"@onecx/angular-remote-components": ["libs/angular-remote-components/src/index.ts"],
+ "@onecx/angular-remote-components/mocks": ["libs/angular-remote-components/mocks/index.ts"],
"@onecx/angular-standalone-shell": ["libs/angular-standalone-shell/src/index.ts"],
"@onecx/angular-testing": ["libs/angular-testing/src/index.ts"],
"@onecx/angular-webcomponents": ["libs/angular-webcomponents/src/index.ts"],