Skip to content

Commit

Permalink
Merged in feature/SHIBUI-1267 (pull request #326)
Browse files Browse the repository at this point in the history
SHIBUI-1267 Implemented configuration component

Approved-by: Ryan Mathis <rmathis@unicon.net>
  • Loading branch information
rmathis committed Jun 19, 2019
2 parents df3abf7 + de3d93b commit 1fa9c6d
Show file tree
Hide file tree
Showing 76 changed files with 2,440 additions and 1,001 deletions.
1 change: 1 addition & 0 deletions backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ label.filter-name=Filter Name
label.filter-enabled=Filter Enabled
label.filter-target=FilterTarget
label.filter-type=Filter Type
label.option=Option
label.value=Value
label.binding-type=Binding Type
label.sign-assertion=Sign Assertions
Expand Down
1 change: 1 addition & 0 deletions backend/src/main/resources/metadata-sources-ui-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@
},
"attributeRelease": {
"type": "array",
"title": "label.attribute-release",
"description": "Attribute release table - select the attributes you want to release (default unchecked)",
"widget": {
"id": "checklist",
Expand Down
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test --code-coverage --source-map=false",
"test": "ng test --code-coverage --source-map=true",
"lint": "ng lint",
"e2e": "ng e2e",
"build:static": "node-sass src/static.scss ./dist/unsecured/static.css",
Expand Down
130 changes: 130 additions & 0 deletions ui/src/app/metadata/configuration/action/configuration.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { Action } from '@ngrx/store';
import { Metadata } from '../../domain/domain.type';
import { Schema } from '../model/schema';
import { Wizard } from '../../../wizard/model';

export enum ConfigurationActionTypes {
LOAD_METADATA_REQUEST = '[Metadata Configuration] Load Metadata Request',
LOAD_METADATA_SUCCESS = '[Metadata Configuration] Load Metadata Success',
LOAD_METADATA_ERROR = '[Metadata Configuration] Load Metadata Error',

LOAD_SCHEMA_REQUEST = '[Metadata Configuration] Load Schema Request',
LOAD_SCHEMA_SUCCESS = '[Metadata Configuration] Load Schema Success',
LOAD_SCHEMA_ERROR = '[Metadata Configuration] Load Schema Error',

LOAD_XML_REQUEST = '[Metadata Configuration] Load XML Request',
LOAD_XML_SUCCESS = '[Metadata Configuration] Load XML Success',
LOAD_XML_ERROR = '[Metadata Configuration] Load XML Error',

SET_METADATA = '[Metadata Configuration] Set Metadata Model',
SET_DEFINITION = '[Metadata Configuration] Set Metadata Definition',
SET_SCHEMA = '[Metadata Configuration] Set Metadata Schema',
SET_XML = '[Metadata Configuration] Set Metadata Xml',

DOWNLOAD_XML = '[Metadata Configuration] Download Metadata Xml',

CLEAR = '[Metadata Configuration] Clear'
}

export class LoadMetadataRequest implements Action {
readonly type = ConfigurationActionTypes.LOAD_METADATA_REQUEST;

constructor(public payload: { id: string, type: string }) { }
}

export class LoadMetadataSuccess implements Action {
readonly type = ConfigurationActionTypes.LOAD_METADATA_SUCCESS;

constructor(public payload: Metadata) { }
}

export class LoadMetadataError implements Action {
readonly type = ConfigurationActionTypes.LOAD_METADATA_ERROR;

constructor(public payload: any) { }
}

export class LoadSchemaRequest implements Action {
readonly type = ConfigurationActionTypes.LOAD_SCHEMA_REQUEST;

constructor(public payload: string) { }
}

export class LoadSchemaSuccess implements Action {
readonly type = ConfigurationActionTypes.LOAD_SCHEMA_SUCCESS;

constructor(public payload: Schema) { }
}

export class LoadSchemaError implements Action {
readonly type = ConfigurationActionTypes.LOAD_SCHEMA_ERROR;

constructor(public payload: any) { }
}

export class LoadXmlRequest implements Action {
readonly type = ConfigurationActionTypes.LOAD_XML_REQUEST;

constructor(public payload: string) { }
}

export class LoadXmlSuccess implements Action {
readonly type = ConfigurationActionTypes.LOAD_XML_SUCCESS;

constructor(public payload: string) { }
}

export class LoadXmlError implements Action {
readonly type = ConfigurationActionTypes.LOAD_XML_ERROR;

constructor(public payload: any) { }
}

export class SetMetadata implements Action {
readonly type = ConfigurationActionTypes.SET_METADATA;

constructor(public payload: Metadata) { }
}

export class SetDefinition implements Action {
readonly type = ConfigurationActionTypes.SET_DEFINITION;

constructor(public payload: Wizard<Metadata>) { }
}

export class SetSchema implements Action {
readonly type = ConfigurationActionTypes.SET_SCHEMA;

constructor(public payload: Schema) { }
}

export class SetXml implements Action {
readonly type = ConfigurationActionTypes.SET_XML;

constructor(public payload: string) { }
}

export class DownloadXml implements Action {
readonly type = ConfigurationActionTypes.DOWNLOAD_XML;
}

export class ClearConfiguration implements Action {
readonly type = ConfigurationActionTypes.CLEAR;
}

export type ConfigurationActionsUnion =
| LoadMetadataRequest
| LoadMetadataSuccess
| LoadMetadataError
| LoadSchemaRequest
| LoadSchemaSuccess
| LoadSchemaError
| LoadXmlRequest
| LoadXmlSuccess
| LoadXmlError
| SetMetadata
| SetDefinition
| SetSchema
| SetXml
| DownloadXml
| ClearConfiguration;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div>
<ng-container *ngIf="property.items.type === 'object'">
<div class="p-2" role="term" [translate]="property.name">{{ property.name }}</div>
<div class="p-2" *ngIf="property.value && property.value.length">
<div *ngFor="let value of property.value; let i = index;" class="mb-2 bg-lighter">
<div *ngFor="let prop of getKeys(property.items); let n = index;"
class="d-flex p-2">
<div class="w-50">
<span [class.invisible]="n">{{ i + 1 }}.&nbsp;</span>
<span [translate]="property.items.properties[prop].title">{{ property.items.properties[prop].title }}</span>
</div>
<div class="w-50" [ngbPopover]="value[prop]" triggers="mouseenter:mouseleave" container="body" placement="left">
{{ value[prop] }}
</div>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="property.items.type === 'string'" [ngSwitch]="getItemType(property.items)">
<ng-container *ngSwitchCase="'datalist'">
<ng-template [ngTemplateOutlet]="listref"></ng-template>
</ng-container>
<ng-container *ngSwitchCase="'select'">
<ng-template [ngTemplateOutlet]="listref"></ng-template>
</ng-container>
<ng-container *ngSwitchDefault>
<div *ngFor="let attr of attributeList$ | async" class="d-flex justify-content-between border-bottom border-light">
<span class="w-50 p-2" role="term" [translate]="attr.label">{{ attr.label }}</span>
<div class="w-50 p-2">
<span *ngIf="property.value && property.value.indexOf(attr.key) > -1" translate="value.true">
true
</span>
<span *ngIf="!property.value || !(property.value.indexOf(attr.key) > -1)" translate="value.false">
false
</span>
</div>
</div>
</ng-container>
</ng-container>
</div>
<ng-template #listref>
<div class="d-flex border-bottom border-light">
<span class="w-50 p-2" role="term" [translate]="property.name">{{ property.name }}</span>
<p class="text-secondary w-50 p-2" *ngIf="!property.value || !property.value.length">&mdash;</p>
<ul class="list-unstyled w-50 py-2" *ngIf="property.value && property.value.length">
<li *ngFor="let item of property.value">
{{ item }}
</li>
</ul>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Component, ViewChild, Input } from '@angular/core';
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap';
import { Property } from '../../domain/model/property';
import { MockI18nModule } from '../../../../testing/i18n.stub';
import { SCHEMA } from '../../../../testing/form-schema.stub';
import { getStepProperty } from '../../domain/utility/configuration';
import { ArrayPropertyComponent } from './array-property.component';
import { AttributesService } from '../../domain/service/attributes.service';
import { MockAttributeService } from '../../../../testing/attributes.stub';
import { of } from 'rxjs';

@Component({
template: `
<array-property [property]="property"></array-property>
`
})
class TestHostComponent {
@ViewChild(ArrayPropertyComponent)
public componentUnderTest: ArrayPropertyComponent;

property: Property = getStepProperty(SCHEMA.properties.list, {
name: 'foo',
type: 'baz',
description: 'foo bar baz',
list: []
}, SCHEMA.definitions);

setProperty(property: Property): void {
this.property = property;
}
}

describe('Array Property Component', () => {

let fixture: ComponentFixture<TestHostComponent>;
let instance: TestHostComponent;
let app: ArrayPropertyComponent;
let service: AttributesService;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
NgbPopoverModule,
MockI18nModule,
RouterTestingModule
],
declarations: [
ArrayPropertyComponent,
TestHostComponent
],
providers: [
{ provide: AttributesService, useClass: MockAttributeService }
]
}).compileComponents();

fixture = TestBed.createComponent(TestHostComponent);
instance = fixture.componentInstance;
app = instance.componentUnderTest;
service = TestBed.get(AttributesService);
fixture.detectChanges();
}));

it('should accept a property input', async(() => {
expect(app).toBeTruthy();
}));

describe('attributeList$ getter', () => {
it('should return an empty list when no data or dataUrl is set', () => {
app.attributeList$.subscribe((list) => {
expect(list).toEqual([]);
});
});
it('should return a list of data items from the schema', () => {
const datalist = [
{ key: 'foo', label: 'foo' },
{ key: 'bar', label: 'bar' },
{ key: 'baz', label: 'baz' },
];
instance.setProperty({
...instance.property,
widget: {
id: 'datalist',
data: datalist
}
});
fixture.detectChanges();
app.attributeList$.subscribe(list => {
expect(list).toEqual(datalist);
});
});

it('should call the attribute service with a provided dataUrl', () => {
const datalist = [
{ key: 'foo', label: 'foo' },
{ key: 'bar', label: 'bar' },
{ key: 'baz', label: 'baz' },
];
spyOn(service, 'query').and.returnValue(of(datalist));
instance.setProperty({
...instance.property,
widget: {
id: 'datalist',
dataUrl: '/foo'
}
});
fixture.detectChanges();
app.attributeList$.subscribe(list => {
expect(list).toEqual(datalist);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Component, Input } from '@angular/core';
import { Property } from '../../domain/model/property';
import { Observable, of } from 'rxjs';
import { AttributesService } from '../../domain/service/attributes.service';
import { ConfigurationPropertyComponent } from './configuration-property.component';

@Component({
selector: 'array-property',
templateUrl: './array-property.component.html',
styleUrls: []
})

export class ArrayPropertyComponent extends ConfigurationPropertyComponent {
@Input() property: Property;

constructor(
private attrService: AttributesService
) {
super();
}

get attributeList$(): Observable<{ key: string, label: string }[]> {
if (this.property.widget && this.property.widget.hasOwnProperty('data')) {
return of(this.property.widget.data);
}
if (this.property.widget && this.property.widget.hasOwnProperty('dataUrl')) {
return this.attrService.query(this.property.widget.dataUrl);
}
return of([]);
}
}

Empty file.
Loading

0 comments on commit 1fa9c6d

Please sign in to comment.