-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
404 additions
and
9 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
src/app/product-store/product-detail/product-apps/product-apps.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<div *ngIf="!apps$"> | ||
<div class="surface-section my-3"> | ||
<p-message severity="error" styleClass="p-2" [text]="'ACTIONS.SEARCH.APP.NOT_FOUND' | translate"></p-message> | ||
</div> | ||
</div> | ||
<ng-container *ngIf="apps$ | async as apps"> | ||
<p-dataView | ||
id="product_detail_apps_dataview" | ||
[value]="apps.stream ? apps.stream : []" | ||
[paginator]="true" | ||
[alwaysShowPaginator]="false" | ||
[rowsPerPageOptions]="[10, 20, 50]" | ||
[rows]="viewMode === 'grid' ? 12 : 10" | ||
[layout]="viewMode" | ||
[emptyMessage]="'ACTIONS.SEARCH.NOT_FOUND' | translate" | ||
filterBy="app_name" | ||
[sortField]="sortField" | ||
[sortOrder]="sortOrder" | ||
> | ||
<ng-template pTemplate="header"> | ||
<ocx-data-view-controls | ||
[enableFiltering]="true" | ||
[enableSorting]="true" | ||
[supportedViews]="['list']" | ||
[initialViewMode]="viewMode" | ||
[sortingOptions]="[{ label: 'APP.APP_NAME' | translate, value: 'app_name' }]" | ||
[defaultSortOption]="sortField" | ||
[defaultSortDirection]="sortOrder === 1" | ||
(dataViewChange)="onLayoutChange($event)" | ||
(filterChange)="onFilterChange($event)" | ||
[filterColumns]="['APP.APP_NAME' | translate]" | ||
(sortChange)="onSortChange($event)" | ||
(sortDirectionChange)="onSortDirChange($event)" | ||
[translations]="dataViewControlsTranslations" | ||
> | ||
</ocx-data-view-controls | ||
></ng-template> | ||
<ng-template let-app pTemplate="listItem"> | ||
<div | ||
class="col-12 grid grid-nogutter align-items-center row-gap-1 listview-row p-1 hover:bg-gray-200 cursor-pointer" | ||
> | ||
<div class="col-12 md:col-5 lg:col-5 xl:col-6"> | ||
<div class="flex flex-column justify-content-center gap-1 text-center md:text-left"> | ||
<div>{{ limitText(app.app_name, 50) }}</div> | ||
</div> | ||
</div> | ||
</div> | ||
</ng-template> | ||
</p-dataView> | ||
</ng-container> |
5 changes: 5 additions & 0 deletions
5
src/app/product-store/product-detail/product-apps/product-apps.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@import '/src/_mixins.scss'; | ||
|
||
@include make-disabled-form-readable-input; | ||
@include make-disabled-form-readable-dropdown; | ||
@include prepare-inputgroup; |
218 changes: 218 additions & 0 deletions
218
src/app/product-store/product-detail/product-apps/product-apps.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
import { NO_ERRORS_SCHEMA } from '@angular/core' | ||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing' | ||
import { HttpClient } from '@angular/common/http' | ||
import { HttpClientTestingModule } from '@angular/common/http/testing' | ||
import { RouterTestingModule } from '@angular/router/testing' | ||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core' | ||
import { of, throwError } from 'rxjs' | ||
import { FormControl, FormGroup, Validators } from '@angular/forms' | ||
|
||
import { PortalMessageService } from '@onecx/portal-integration-angular' | ||
import { HttpLoaderFactory } from 'src/app/shared/shared.module' | ||
import { ProductPropertyComponent, ProductDetailForm } from './product-props.component' | ||
import { ProductsAPIService } from 'src/app/generated' | ||
|
||
describe('ProductPropertyComponent', () => { | ||
let component: ProductPropertyComponent | ||
let fixture: ComponentFixture<ProductPropertyComponent> | ||
|
||
const msgServiceSpy = jasmine.createSpyObj<PortalMessageService>('PortalMessageService', ['success', 'error', 'info']) | ||
const apiServiceSpy = { | ||
createProduct: jasmine.createSpy('createProduct').and.returnValue(of({})), | ||
updateProduct: jasmine.createSpy('updateProduct').and.returnValue(of({})) | ||
} | ||
|
||
beforeEach(waitForAsync(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [ProductPropertyComponent], | ||
imports: [ | ||
HttpClientTestingModule, | ||
RouterTestingModule, | ||
TranslateModule.forRoot({ | ||
loader: { | ||
provide: TranslateLoader, | ||
useFactory: HttpLoaderFactory, | ||
deps: [HttpClient] | ||
} | ||
}) | ||
], | ||
schemas: [NO_ERRORS_SCHEMA], | ||
providers: [ | ||
{ provide: ProductsAPIService, useValue: apiServiceSpy }, | ||
{ provide: PortalMessageService, useValue: msgServiceSpy } | ||
] | ||
}).compileComponents() | ||
})) | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(ProductPropertyComponent) | ||
component = fixture.componentInstance | ||
fixture.detectChanges() | ||
}) | ||
|
||
afterEach(() => { | ||
msgServiceSpy.success.calls.reset() | ||
msgServiceSpy.error.calls.reset() | ||
msgServiceSpy.info.calls.reset() | ||
apiServiceSpy.createProduct.calls.reset() | ||
apiServiceSpy.updateProduct.calls.reset() | ||
}) | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy() | ||
}) | ||
|
||
it('should patchValue in formGroup onChanges if product', () => { | ||
const product = { | ||
id: 'id', | ||
name: 'name', | ||
basePath: 'path' | ||
} | ||
component.product = product | ||
spyOn(component.formGroup, 'patchValue') | ||
|
||
component.ngOnChanges() | ||
|
||
expect(component.formGroup.patchValue).toHaveBeenCalledWith({ ...product }) | ||
expect(component.product.name).toEqual(product.name) | ||
}) | ||
|
||
it('should reset formGroup onChanges if no product', () => { | ||
spyOn(component.formGroup, 'reset') | ||
|
||
component.ngOnChanges() | ||
|
||
expect(component.formGroup.reset).toHaveBeenCalled() | ||
}) | ||
|
||
it('should call createProduct onSubmit in new mode', () => { | ||
apiServiceSpy.createProduct.and.returnValue(of({})) | ||
const formGroup = new FormGroup<ProductDetailForm>({ | ||
id: new FormControl<string | null>('id'), | ||
name: new FormControl<string | null>('name'), | ||
operator: new FormControl<boolean | null>(null), | ||
version: new FormControl<string | null>('version'), | ||
description: new FormControl<string | null>(null), | ||
imageUrl: new FormControl<string | null>(null), | ||
basePath: new FormControl<string | null>('path'), | ||
displayName: new FormControl<string | null>('display'), | ||
iconName: new FormControl<string | null>('icon'), | ||
classifications: new FormControl<string[] | null>(null) | ||
}) | ||
component.formGroup = formGroup as FormGroup<ProductDetailForm> | ||
component.changeMode = 'CREATE' | ||
|
||
component.onSubmit() | ||
|
||
expect(apiServiceSpy.createProduct).toHaveBeenCalled() | ||
expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.CREATE.PRODUCT.OK' }) | ||
}) | ||
|
||
it('should call updateProduct onSubmit in edit mode', () => { | ||
apiServiceSpy.updateProduct.and.returnValue(of({})) | ||
const formGroup = new FormGroup<ProductDetailForm>({ | ||
id: new FormControl<string | null>('id'), | ||
name: new FormControl<string | null>('name'), | ||
operator: new FormControl<boolean | null>(null), | ||
version: new FormControl<string | null>('version'), | ||
description: new FormControl<string | null>(null), | ||
imageUrl: new FormControl<string | null>(null), | ||
basePath: new FormControl<string | null>('path'), | ||
displayName: new FormControl<string | null>('display'), | ||
iconName: new FormControl<string | null>('icon'), | ||
classifications: new FormControl<string[] | null>(null) | ||
}) | ||
component.formGroup = formGroup as FormGroup<ProductDetailForm> | ||
component.changeMode = 'EDIT' | ||
|
||
component.onSubmit() | ||
|
||
expect(apiServiceSpy.updateProduct).toHaveBeenCalled() | ||
expect(msgServiceSpy.success).toHaveBeenCalledWith({ summaryKey: 'ACTIONS.EDIT.PRODUCT.OK' }) | ||
}) | ||
|
||
it('should display error if updateProduct fails', () => { | ||
apiServiceSpy.updateProduct.and.returnValue(throwError(() => new Error())) | ||
const formGroup = new FormGroup<ProductDetailForm>({ | ||
id: new FormControl<string | null>('id'), | ||
name: new FormControl<string | null>('name'), | ||
operator: new FormControl<boolean | null>(null), | ||
version: new FormControl<string | null>('version'), | ||
description: new FormControl<string | null>(null), | ||
imageUrl: new FormControl<string | null>(null), | ||
basePath: new FormControl<string | null>('path'), | ||
displayName: new FormControl<string | null>('display'), | ||
iconName: new FormControl<string | null>('icon'), | ||
classifications: new FormControl<string[] | null>(null) | ||
}) | ||
component.formGroup = formGroup as FormGroup<ProductDetailForm> | ||
component.changeMode = 'EDIT' | ||
|
||
component.onSubmit() | ||
|
||
expect(component.formGroup.valid).toBeTrue() | ||
expect(msgServiceSpy.error).toHaveBeenCalledWith({ | ||
summaryKey: 'ACTIONS.EDIT.PRODUCT.NOK' | ||
}) | ||
}) | ||
|
||
it('should display error if createProduct fails', () => { | ||
apiServiceSpy.createProduct.and.returnValue(throwError(() => new Error())) | ||
const formGroup = new FormGroup<ProductDetailForm>({ | ||
id: new FormControl<string | null>('id'), | ||
name: new FormControl<string | null>('name'), | ||
operator: new FormControl<boolean | null>(null), | ||
version: new FormControl<string | null>('version'), | ||
description: new FormControl<string | null>(null), | ||
imageUrl: new FormControl<string | null>(null), | ||
basePath: new FormControl<string | null>('path'), | ||
displayName: new FormControl<string | null>('display'), | ||
iconName: new FormControl<string | null>('icon'), | ||
classifications: new FormControl<string[] | null>(null) | ||
}) | ||
component.formGroup = formGroup as FormGroup<ProductDetailForm> | ||
component.changeMode = 'CREATE' | ||
|
||
component.onSubmit() | ||
|
||
expect(component.formGroup.valid).toBeTrue() | ||
expect(msgServiceSpy.error).toHaveBeenCalledWith({ | ||
summaryKey: 'ACTIONS.CREATE.PRODUCT.NOK' | ||
}) | ||
}) | ||
|
||
it('should display error onSubmit if formGroup invalid', () => { | ||
const formGroup = new FormGroup<ProductDetailForm>({ | ||
id: new FormControl<string | null>(null, Validators.required), | ||
name: new FormControl<string | null>('name'), | ||
operator: new FormControl<boolean | null>(null), | ||
version: new FormControl<string | null>('version'), | ||
description: new FormControl<string | null>(null), | ||
imageUrl: new FormControl<string | null>(null), | ||
basePath: new FormControl<string | null>('path'), | ||
displayName: new FormControl<string | null>('display'), | ||
iconName: new FormControl<string | null>('icon'), | ||
classifications: new FormControl<string[] | null>(null) | ||
}) | ||
component.formGroup = formGroup as FormGroup<ProductDetailForm> | ||
|
||
component.onSubmit() | ||
|
||
expect(component.formGroup.valid).toBeFalse() | ||
expect(msgServiceSpy.error).toHaveBeenCalledWith({ | ||
summaryKey: 'VALIDATION.FORM_INVALID' | ||
}) | ||
}) | ||
|
||
it('should display error onSubmit if formGroup invalid', () => { | ||
const event = { | ||
target: { | ||
files: ['file'] | ||
} | ||
} | ||
|
||
component.onFileUpload(event as any, 'logo') | ||
|
||
expect(component.formGroup.valid).toBeFalse() | ||
}) | ||
}) |
66 changes: 66 additions & 0 deletions
66
src/app/product-store/product-detail/product-apps/product-apps.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Component, Input, OnChanges, ViewChild } from '@angular/core' | ||
import { SelectItem } from 'primeng/api' | ||
import { Observable, finalize } from 'rxjs' | ||
|
||
import { DataViewControlTranslations, PortalMessageService } from '@onecx/portal-integration-angular' | ||
import { Product, MicrofrontendsAPIService, MicrofrontendPageResult } from '../../../generated' | ||
import { dropDownSortItemsByLabel, limitText } from 'src/app/shared/utils' | ||
import { IconService } from '../../../shared/iconservice' | ||
|
||
@Component({ | ||
selector: 'ps-product-apps', | ||
templateUrl: './product-apps.component.html', | ||
styleUrls: ['./product-apps.component.scss'] | ||
}) | ||
export class ProductAppsComponent implements OnChanges { | ||
@Input() product: Product | undefined | ||
@Input() dateFormat = 'medium' | ||
@Input() changeMode = 'VIEW' | ||
public apps$!: Observable<MicrofrontendPageResult> | ||
public iconItems: SelectItem[] = [{ label: '', value: null }] | ||
public viewMode = 'list' | ||
public filter: string | undefined | ||
public sortField = 'name' | ||
public sortOrder = 1 | ||
public searchInProgress = false | ||
public limitText = limitText | ||
public dataViewControlsTranslations: DataViewControlTranslations = {} | ||
@ViewChild(DataView) dv: DataView | undefined | ||
|
||
constructor( | ||
private icon: IconService, | ||
private appApi: MicrofrontendsAPIService, | ||
private msgService: PortalMessageService | ||
) { | ||
this.iconItems.push(...this.icon.icons.map((i) => ({ label: i, value: i }))) | ||
this.iconItems.sort(dropDownSortItemsByLabel) | ||
} | ||
|
||
ngOnChanges(): void { | ||
console.log('apps ngOnChanges') | ||
this.loadApps() | ||
} | ||
|
||
public loadApps(): void { | ||
this.searchInProgress = true | ||
this.apps$ = this.appApi | ||
.searchMicrofrontends({ | ||
microfrontendSearchCriteria: { productName: this.product?.name, pageSize: 1000 } | ||
}) | ||
.pipe(finalize(() => (this.searchInProgress = false))) | ||
} | ||
|
||
public onLayoutChange(viewMode: string): void { | ||
this.viewMode = viewMode | ||
} | ||
public onFilterChange(filter: string): void { | ||
this.filter = filter | ||
//this.dv?.filter(filter, 'contains') | ||
} | ||
public onSortChange(field: string): void { | ||
this.sortField = field | ||
} | ||
public onSortDirChange(asc: boolean): void { | ||
this.sortOrder = asc ? -1 : 1 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/app/product-store/product-detail/product-detail.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/app/product-store/product-detail/product-intern/product-intern.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.