Skip to content
Permalink
Browse files
feat(list-editor): insert a child node at a specific position (DSP-1301
…) (#395)

* feat(list-editor): adds functionality for inserting a child node at a specific position

* feat(list-editor): adds support for inserting a node within a list of child nodes

* chore(package.json): update DSP-JS version

* test(edit-list-item): adds unit test for insertChildNode method

* test(list-item): adds test for insert node switch case

* chore: align long list of imports into a list
  • Loading branch information
mdelez committed Feb 26, 2021
1 parent 040df19 commit 5107200dc769d5690f5356bb467f05143a05abd1

Some generated files are not rendered by default. Learn more.

@@ -31,7 +31,7 @@
"@angular/platform-browser-dynamic": "~9.1.12",
"@angular/router": "~9.1.12",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^2.0.2",
"@dasch-swiss/dsp-js": "^2.1.0",
"@dasch-swiss/dsp-ui": "^1.2.2",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
@@ -145,6 +145,13 @@
(updateParent)="replaceTitle($event)"></app-list-info-form>
</div>

<!-- Insert list child node -->
<div *ngSwitchCase="'insertListNode'">
<app-dialog-header [title]="data.title" [subtitle]="'Insert new child node'">
</app-dialog-header>
<app-edit-list-item [iri]="data.id" [projectIri]="data.project" mode="insert" [projectCode]="data.projectCode" [parentIri]="data.parentIri" [position]="data.position" (closeDialog)="dialogRef.close($event)"></app-edit-list-item>
</div>

<!-- Update list info -->
<div *ngSwitchCase="'editListInfo'">
<app-dialog-header [title]="data.title" [subtitle]="'Edit list info'">
@@ -157,7 +164,7 @@
<div *ngSwitchCase="'editListNode'">
<app-dialog-header [title]="data.title" [subtitle]="'Edit child node'">
</app-dialog-header>
<app-edit-list-item [iri]="data.id" [projectIri]="data.project" (closeDialog)="dialogRef.close($event)"></app-edit-list-item>
<app-edit-list-item [iri]="data.id" [projectIri]="data.project" mode="update" (closeDialog)="dialogRef.close($event)"></app-edit-list-item>
</div>

<!-- Delete list node-->
@@ -37,8 +37,8 @@
type="submit"
color="primary"
[disabled]="saveButtonDisabled"
(click)="updateChildNode()">
{{ 'appLabels.form.action.update' | translate }}
(click)=" mode === 'editListNode' ? updateChildNode() : insertChildNode()">
{{ mode === 'editListNode' ? 'appLabels.form.action.update' : 'appLabels.form.action.submit' | translate }}
</button>
</span>
</div>

Large diffs are not rendered by default.

@@ -1,5 +1,16 @@
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
import { ApiResponseData, ApiResponseError, ChildNodeInfoResponse, KnoraApiConnection, List, ListNodeInfo, ListNodeInfoResponse, StringLiteral, UpdateChildNodeRequest } from '@dasch-swiss/dsp-js';
import {
ApiResponseData,
ApiResponseError,
ChildNodeInfoResponse,
CreateChildNodeRequest,
KnoraApiConnection,
List,
ListNodeInfo,
ListNodeInfoResponse,
StringLiteral,
UpdateChildNodeRequest
} from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken } from '@dasch-swiss/dsp-ui';

@Component({
@@ -12,6 +23,14 @@ export class EditListItemComponent implements OnInit {

@Input() iri: string;

@Input() mode: 'insert' | 'update';

@Input() parentIri?: string;

@Input() position?: number;

@Input() projectCode?: string;

@Input() projectIri: string;

@Output() closeDialog: EventEmitter<List | ListNodeInfo> = new EventEmitter<List>();
@@ -46,16 +65,23 @@ export class EditListItemComponent implements OnInit {
ngOnInit(): void {
this.loading = true;

// get list
this._dspApiConnection.admin.listsEndpoint.getListNodeInfo(this.iri).subscribe(
(response: ApiResponseData<ListNodeInfoResponse>) => {
this.listNode = response.body.nodeinfo;
this.buildForm(response.body.nodeinfo);
},
(error: ApiResponseError) => {
console.error(error);
}
);
// if updating a node, get the existing node info
if (this.mode === 'update') {
this._dspApiConnection.admin.listsEndpoint.getListNodeInfo(this.iri).subscribe(
(response: ApiResponseData<ListNodeInfoResponse>) => {
this.listNode = response.body.nodeinfo;
this.buildForm(response.body.nodeinfo);
},
(error: ApiResponseError) => {
console.error(error);
}
);
} else {
this.labels = [];
this.comments = [];

this.loading = false;
}
}

/**
@@ -105,8 +131,8 @@ export class EditListItemComponent implements OnInit {
}

/**
* Called from the template when the 'update' button is clicked.
* Sends a request to DSP-API to update the list node with the data inside the two local arrays.
* Called from the template when the 'submit' button is clicked in update mode.
* Sends a request to DSP-API to update the list child node with the data inside the two local arrays.
*/
updateChildNode() {
const childNodeUpdateData: UpdateChildNodeRequest = new UpdateChildNodeRequest();
@@ -127,4 +153,30 @@ export class EditListItemComponent implements OnInit {
);
}

/**
* Called from the template when the 'submit' button is clicked in insert mode.
* Sends a request to DSP-API to insert a new list child node in the provided position.
*/
insertChildNode() {
const createChildNodeRequest: CreateChildNodeRequest = new CreateChildNodeRequest();

createChildNodeRequest.name = this.projectCode + '-' + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
createChildNodeRequest.parentNodeIri = this.parentIri;
createChildNodeRequest.labels = this.labels;
createChildNodeRequest.comments = this.comments.length > 0 ? this.comments : [];
createChildNodeRequest.projectIri = this.projectIri;
createChildNodeRequest.position = this.position;

this._dspApiConnection.admin.listsEndpoint.createChildNode(createChildNodeRequest).subscribe(
(response: ApiResponseData<ListNodeInfoResponse>) => {
this.loading = false;
this.closeDialog.emit(response.body.nodeinfo);
},
(error: ApiResponseError) => {
this.errorMessage = error;
this.loading = false;
}
);
}

}
@@ -1,5 +1,5 @@
<!-- add new node item -->
<div class="new-list-item medium-field" *ngIf="parentIri && projectIri">
<div class="new-list-item medium-field" *ngIf="newNode">
<dsp-string-literal-input class="list-item-label" [placeholder]="placeholder" [value]="[]"
(dataChanged)="handleData($event)" [language]="language" (enter)="createChildNode()">
</dsp-string-literal-input>
@@ -14,7 +14,7 @@

<!-- node item -->
<div class="list-item medium-field"
*ngIf="!(parentIri && projectIri) && labels"
*ngIf="!newNode"
(mouseenter)="mouseEnter()"
(mouseleave)="mouseLeave()">
<dsp-string-literal-input class="list-item-label"
@@ -37,6 +37,12 @@
class="reposition down">
<mat-icon>arrow_downward</mat-icon>
</button>
<button mat-button
title="insert new child node above"
(click)="$event.stopPropagation(); openDialog('insertListNode', labels[0].value, iri)"
class="insert">
<mat-icon>vertical_align_top</mat-icon>
</button>
<button mat-button
class="edit"
title="edit"
@@ -11,15 +11,14 @@ import {
ListInfoResponse,
ListNode,
ListNodeInfoResponse,
RepositionChildNodeRequest,
StringLiteral
} from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken } from '@dasch-swiss/dsp-ui';
import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';

export class ListNodeOperation {
operation: 'create' | 'update' | 'delete' | 'reposition';
operation: 'create' | 'insert' | 'update' | 'delete' | 'reposition';
listNode: ListNode;
}

@@ -82,6 +81,8 @@ export class ListItemFormComponent implements OnInit {
// is this node in the last position of the list
@Input() lastPosition: boolean = false;

@Input() newNode: boolean = false;

@Output() refreshParent: EventEmitter<ListNodeOperation> = new EventEmitter<ListNodeOperation>();

loading: boolean;
@@ -107,7 +108,7 @@ export class ListItemFormComponent implements OnInit {
}

// it can be used in the input placeholder
if (this.parentIri) {
if (this.newNode) {
this._dspApiConnection.admin.listsEndpoint.getListNodeInfo(this.parentIri).subscribe(
(response: ApiResponseData<ListNodeInfoResponse | ListInfoResponse>) => {
if (response.body instanceof ListInfoResponse) { // root node
@@ -211,12 +212,21 @@ export class ListItemFormComponent implements OnInit {
* @param iri iri of the node.
*/
openDialog(mode: string, name: string, iri?: string): void {

const dialogConfig: MatDialogConfig = {
width: '640px',
position: {
top: '112px'
},
data: { mode: mode, title: name, id: iri, project: this.projectIri }
data: {
mode: mode,
title: mode === 'editListNode' ? name : 'Insert new child node',
id: iri,
project: this.projectIri,
projectCode: this.projectcode,
parentIri: this.parentIri,
position: this.position
}
};

// open the dialog box
@@ -226,19 +236,24 @@ export class ListItemFormComponent implements OnInit {
);

dialogRef.afterClosed().subscribe((data: ChildNodeInfo | boolean) => {

// init data to emit to parent
const listNodeOperation = new ListNodeOperation();

if (data instanceof ChildNodeInfo) { // update
if (mode === 'insertListNode' && data) {
// the call to DSP-API to insert the new node is done in the child component
listNodeOperation.listNode = (data as ListNode);
listNodeOperation.operation = 'insert';

this.refreshParent.emit(listNodeOperation);
} else if (mode === 'editListNode' && data) { // update
// the call to DSP-API to update the node is done in the child component
listNodeOperation.listNode = (data as ListNode);
listNodeOperation.operation = 'update';

// emit data to parent to update the view
this.refreshParent.emit(listNodeOperation);
this.labels = data.labels;
} else if (typeof(data) === 'boolean' && data === true) { // delete
this.labels = (data as ChildNodeInfo).labels;
} else if (mode === 'deleteListNode' && typeof(data) === 'boolean' && data === true) { // delete
// delete the node
this._dspApiConnection.admin.listsEndpoint.deleteListNode(iri).subscribe(
(response: ApiResponseData<DeleteListNodeResponse>) => {
@@ -272,6 +287,10 @@ export class ListItemFormComponent implements OnInit {
});
}

/**
* Called from the template when either of the two reposition buttons is clicked
* @param direction in which direction the node should move
*/
repositionNode(direction: 'up' | 'down') {
const listNodeOperation = new ListNodeOperation();

@@ -11,7 +11,7 @@
<!-- existing node: show label in form; value is e.g. {{node.labels[0].value}} -->
<app-list-item-form [iri]="node.id" [language]="language" (refreshParent)="updateView($event, true)"
[projectIri]="projectIri" [projectcode]="projectcode" [labels]="node.labels"
[position]="node.position" [lastPosition]="last">
[position]="node.position" [lastPosition]="last" [parentIri]="parentIri">
</app-list-item-form>


@@ -26,15 +26,15 @@
<div *ngIf="node.id === expandedNode && node.children.length === 0" class="child-node">
<!-- first child should have an empty list? yes -->
<app-list-item-form class="append-child-node" [parentIri]="node.id" [projectIri]="projectIri"
[projectcode]="projectcode" [language]="language"
[projectcode]="projectcode" [language]="language" [newNode]="true"
(refreshParent)="updateView($event, true)">
</app-list-item-form>
</div>

<!-- form to append new item to parent node -->
<app-list-item-form class="list-node append-child-node" *ngIf="last" [parentIri]="parentIri"
[projectIri]="projectIri" [projectcode]="projectcode" [language]="language"
(refreshParent)="updateView($event)">
[newNode]="true" (refreshParent)="updateView($event)">
</app-list-item-form>

</div>
Loading

0 comments on commit 5107200

Please sign in to comment.