Skip to content

Commit

Permalink
feat(resource): optimize resource instance form (DSP-1256) (#518)
Browse files Browse the repository at this point in the history
* feat(resource): optimize resource instance form

* refactor(resource): clean up code and fix tests

* fix(resource): replace dsp comps by app comps

* fix(properties): adds canDelete input

Co-authored-by: mdelez <60604010+mdelez@users.noreply.github.com>
  • Loading branch information
kilchenmann and mdelez committed Sep 6, 2021
1 parent e7a2c4f commit 5151677
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 216 deletions.
5 changes: 5 additions & 0 deletions src/app/main/directive/base-value.directive.ts
Expand Up @@ -31,6 +31,11 @@ export abstract class BaseValueDirective {
*/
@Input() valueRequiredValidator = true;

/**
* disable the comment field
*/
@Input() commentDisabled? = false;

shouldShowComment = false;

/**
Expand Down
Expand Up @@ -117,23 +117,24 @@ <h3 class="label mat-title">
<div class="property-value">
<!-- the value(s) of the property -->
<div *ngFor="let val of prop.values">
<dsp-display-edit *ngIf="val"
<app-display-edit *ngIf="val"
#displayEdit
[parentResource]="resource.res"
[displayValue]="val"
[propArray]="resource.resProps"
[canDelete]="deleteValueIsAllowed(prop)"
(referredResourceClicked)="openResource($event)"
(referredResourceHovered)="previewResource($event)">
</dsp-display-edit>
</app-display-edit>
</div>
<!-- Add value form -->
<div *ngIf="addValueFormIsVisible && propID === prop.propDef.id">
<dsp-add-value #addValue
<app-add-value #addValue
class="add-value"
[parentResource]="resource.res"
[resourcePropertyDefinition]="$any(resource.res.entityInfo.properties[prop.propDef.id])"
(operationCancelled)="hideAddValueForm()">
</dsp-add-value>
</app-add-value>
</div>
<!-- Add button -->
<div *ngIf="addValueIsAllowed(prop)">
Expand Down
Expand Up @@ -28,14 +28,14 @@ import {
EmitEvent,
Events,
PropertyInfoValues,
UserService,
ValueOperationEventService
} from '@dasch-swiss/dsp-ui';
import { of, Subscription } from 'rxjs';
import { DspResource } from '../dsp-resource';
import { PropertiesComponent } from './properties.component';
import { IncomingService } from '../incoming.service';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { UserService } from '../services/user.service';

/**
* test host component to simulate parent component.
Expand Down
38 changes: 25 additions & 13 deletions src/app/workspace/resource/properties/properties.component.ts
@@ -1,5 +1,6 @@
import { Component, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator';
import {
ApiResponseData,
ApiResponseError,
Expand All @@ -25,24 +26,23 @@ import {
UpdateResourceMetadataResponse,
UserResponse
} from '@dasch-swiss/dsp-js';
import {
AddedEventValue,
DeletedEventValue,
Events,
UpdatedEventValues,
UserService,
ValueOperationEventService,
ValueService
} from '@dasch-swiss/dsp-ui';
import { Subscription } from 'rxjs';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { ConfirmationWithComment, DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
import { NotificationService } from 'src/app/main/services/notification.service';
import { DspResource } from '../dsp-resource';
import { RepresentationConstants } from '../representation/file-representation';
import { IncomingService } from '../incoming.service';
import { PageEvent } from '@angular/material/paginator';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { NotificationService } from 'src/app/main/services/notification.service';
import { RepresentationConstants } from '../representation/file-representation';
import { UserService } from '../services/user.service';
import {
AddedEventValue,
DeletedEventValue,
Events,
UpdatedEventValues,
ValueOperationEventService
} from '../services/value-operation-event.service';
import { ValueService } from '../services/value.service';

// object of property information from ontology class, properties and property values
export interface PropertyInfoValues {
Expand Down Expand Up @@ -434,6 +434,18 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
}
}

/**
* given a resource property, check if its cardinality allows a value to be deleted
*
* @param prop the resource property
*/
deleteValueIsAllowed(prop: PropertyInfoValues): boolean {
const isAllowed = CardinalityUtil.deleteValueForPropertyAllowed(
prop.propDef.id, prop.values.length, this.resource.res.entityInfo.classes[this.resource.res.type]);

return isAllowed;
}

/**
* updates the UI in the event of an existing value being deleted
*
Expand Down
@@ -1,5 +1,6 @@
<!-- Step1: form to select one project, one ontology, one resource with label -->
<form *ngIf="selectResourceForm && showNextStepForm" [formGroup]="selectResourceForm" class="resource-instance-form stepOne form-content" (ngSubmit)="nextStep()">
<!-- Step1: form to select one project, one ontology, one resource -->
<form *ngIf="selectResourceForm && showNextStepForm" [formGroup]="selectResourceForm"
class="resource-instance-form stepOne form-content" (ngSubmit)="nextStep()">

<!-- select one project -->
<div *ngIf="usersProjects?.length > 0">
Expand All @@ -14,8 +15,7 @@
<!-- select one ontology -->
<span>
<div *ngIf="ontologiesMetadata?.ontologies.length > 0">
<app-select-ontology
#selectOntology
<app-select-ontology #selectOntology
[formGroup]="selectResourceForm"
[ontologiesMetadata]="ontologiesMetadata"
[selectedOntology]="selectedOntology"
Expand All @@ -26,14 +26,11 @@

<!-- select one resource -->
<span *ngIf="resourceClasses?.length > 0">
<app-select-resource-class
#selectResourceClass
<app-select-resource-class #selectResourceClass
[formGroup]="selectResourceForm"
[resourceClassDefinitions]="resourceClasses"
[selectedResourceClass]="selectedResourceClass"
[chosenResourceLabel]="resourceLabel"
(resourceClassSelected)="selectProperties($event)"
(resourceLabel)="getResourceLabel($event)">
(resourceClassSelected)="selectProperties($event)">
</app-select-resource-class>
</span>

Expand All @@ -42,20 +39,16 @@
</span>

<!-- action buttons: cancel and next -->
<div class="form-panel large-field">
<div class="form-panel form-action">
<span>
<button mat-button type="button" (click)="closeDialog.emit()">
{{ 'appLabels.form.action.cancel' | translate }}
</button>
</span>
<span class="fill-remaining-space"></span>
<span>
<button
mat-raised-button
type="button"
color="primary"
[disabled]="!selectResourceForm.valid || this.errorMessage"
(click)="nextStep()" class="form-next">
<button mat-raised-button type="button" color="primary"
[disabled]="!selectResourceForm.valid || this.errorMessage" (click)="nextStep()" class="form-next">
Next
</button>
</span>
Expand All @@ -67,11 +60,14 @@
<form *ngIf="propertiesParentForm && !showNextStepForm" [formGroup]="propertiesParentForm" class="resource-instance-form stepTwo form-content" (ngSubmit)="submitData()" appInvalidControlScroll>

<!-- upload file -->
<app-upload *ngIf="hasFileValue" [parentForm]="propertiesParentForm" [representation]="hasFileValue" (fileInfo)="setFileValue($event)"></app-upload>
<app-upload *ngIf="hasFileValue"
[parentForm]="propertiesParentForm"
[representation]="hasFileValue"
(fileInfo)="setFileValue($event)">
</app-upload>

<!-- create property values -->
<app-select-properties
#selectProps
<app-select-properties #selectProps
[ontologyInfo]="ontologyInfo"
[resourceClass]="selectedResourceClass"
[properties]="properties"
Expand All @@ -80,10 +76,10 @@
</app-select-properties>

<!-- action buttons: previous, cancel and save -->
<div class="form-panel large-field btn-field">
<div class="form-panel form-action">
<span>
<button mat-button type="button" (click)="prevStep($event)">
{{ 'appLabels.form.action.back' | translate }}
&larr;&nbsp;{{ 'appLabels.form.action.back' | translate }}
</button>
</span>
<button mat-button type="button" (click)="closeDialog.emit()">
Expand Down
@@ -1,10 +1,17 @@
.stepTwo {
margin: 24px 56px !important;
width: calc(100% - 144px);

.btn-field {
margin-top: 48px;
margin-bottom: 0;
}
}

.btn-field {
width: 600px !important;
margin-left: 48px;
.form-action {
width: 100%;
margin-top: 24px;
// margin-left: 48px;
}

.errorIfNoElement {
Expand Down
Expand Up @@ -17,6 +17,7 @@ import {
ApiResponseData,
CreateIntValue,
CreateResource,
CreateTextValueAsString,
CreateValue,
MockOntology,
MockProjects,
Expand All @@ -35,7 +36,7 @@ import {
UsersEndpointAdmin
} from '@dasch-swiss/dsp-js';
import { OntologyCache } from '@dasch-swiss/dsp-js/src/cache/ontology-cache/OntologyCache';
import { DspActionModule, IntValueComponent, ValueService } from '@dasch-swiss/dsp-ui';
import { DspActionModule, IntValueComponent, TextValueAsStringComponent, ValueService } from '@dasch-swiss/dsp-ui';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { AjaxResponse } from 'rxjs/ajax';
Expand Down Expand Up @@ -75,6 +76,7 @@ class TestHostComponent implements OnInit {
class MockSelectProjectComponent implements OnInit {
@Input() formGroup: FormGroup;
@Input() usersProjects: StoredProject[];
@Input() selectedProject?: string;
@Output() projectSelected = new EventEmitter<string>();

form: FormGroup;
Expand Down Expand Up @@ -102,6 +104,7 @@ class MockSelectProjectComponent implements OnInit {
class MockSelectOntologyComponent implements OnInit {
@Input() formGroup: FormGroup;
@Input() ontologiesMetadata: OntologiesMetadata;
@Input() selectedOntology?: string;
@Output() ontologySelected = new EventEmitter<string>();

form: FormGroup;
Expand Down Expand Up @@ -129,16 +132,15 @@ class MockSelectOntologyComponent implements OnInit {
class MockSelectResourceClassComponent implements OnInit {
@Input() formGroup: FormGroup;
@Input() resourceClassDefinitions: ResourceClassDefinition[];
@Input() selectedResourceClass?: string;

label: string;
form: FormGroup;

constructor(@Inject(FormBuilder) private _fb: FormBuilder) { }

ngOnInit() {
this.form = this._fb.group({
resources: [null, Validators.required],
label: [null, Validators.required]
resources: [null, Validators.required]
});

resolvedPromise.then(() => {
Expand All @@ -152,11 +154,23 @@ class MockSelectResourceClassComponent implements OnInit {
* mock select-properties component to use in tests.
*/
@Component({
selector: 'app-select-properties'
selector: 'app-select-properties',
template: `
<app-text-value-as-string #createVal
[mode]="'create'"
[commentDisabled]="true"
[valueRequiredValidator]="true"
[parentForm]="parentForm"
[formName]="'label'">
</app-text-value-as-string>
`
})
class MockSelectPropertiesComponent {
@ViewChildren('switchProp') switchPropertiesComponent: QueryList<SwitchPropertiesComponent>;

// input for resource's label
@ViewChild('createVal') createValueComponent: BaseValueDirective;

@Input() properties: ResourcePropertyDefinition[];

@Input() ontologyInfo: ResourceClassAndPropertyDefinitions;
Expand Down Expand Up @@ -231,6 +245,46 @@ class MockCreateIntValueComponent implements OnInit {
updateCommentVisibility(): void { }
}

/**
* mock value component to use in tests.
*/
@Component({
selector: 'app-text-value-as-string'
})
class MockCreateTextValueComponent implements OnInit {

@ViewChild('createVal') createValueComponent: TextValueAsStringComponent;

@Input() parentForm: FormGroup;

@Input() formName: string;

@Input() mode;

@Input() displayValue;

@Input() commentDisabled?: boolean;

@Input() valueRequiredValidator: boolean;

form: FormGroup;

valueFormControl: FormControl;
constructor(@Inject(FormBuilder) private _fb: FormBuilder) { }
ngOnInit(): void {
this.valueFormControl = new FormControl(null, [Validators.required]);
this.form = this._fb.group({
label: this.valueFormControl
});
}
getNewValue(): CreateValue {
const createTextVal = new CreateTextValueAsString();
createTextVal.text = 'My Label';
return createTextVal;
}
updateCommentVisibility(): void { }
}

describe('ResourceInstanceFormComponent', () => {
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture<TestHostComponent>;
Expand Down Expand Up @@ -264,7 +318,8 @@ describe('ResourceInstanceFormComponent', () => {
MockSelectResourceClassComponent,
MockSelectPropertiesComponent,
MockSwitchPropertiesComponent,
MockCreateIntValueComponent
MockCreateIntValueComponent,
MockCreateTextValueComponent
],
imports: [
BrowserAnimationsModule,
Expand Down Expand Up @@ -462,7 +517,6 @@ describe('ResourceInstanceFormComponent', () => {
expect(selectResourceClassComp).toBeTruthy();

(selectResourceClassComp.componentInstance as MockSelectResourceClassComponent).form.controls.resources.setValue('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing');
(selectResourceClassComp.componentInstance as MockSelectResourceClassComponent).form.controls.label.setValue('My Label');

testHostComponent.resourceInstanceFormComponent.selectedResourceClass = (selectResourceClassComp.componentInstance as MockSelectResourceClassComponent).resourceClassDefinitions[1];

Expand Down Expand Up @@ -521,6 +575,9 @@ describe('ResourceInstanceFormComponent', () => {

expect(selectPropertiesComp).toBeTruthy();

const label = new CreateTextValueAsString();
label.text = 'My Label';

const props = {};
const createVal = new CreateIntValue();
createVal.int = 123;
Expand Down

0 comments on commit 5151677

Please sign in to comment.