diff --git a/package-lock.json b/package-lock.json index 184f15af9e..58ddfe4dfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32706,4 +32706,4 @@ "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==" } } -} +} \ No newline at end of file diff --git a/src/app/main/dialog/dialog.component.html b/src/app/main/dialog/dialog.component.html index 44bcfca612..6e45243c48 100644 --- a/src/app/main/dialog/dialog.component.html +++ b/src/app/main/dialog/dialog.component.html @@ -160,10 +160,37 @@ + +
+ + Do you want to delete this node? + + + + + +
+ + +
+ Unable to delete this list node as it is in use. + + + + +
+
- - + Do you want to delete this list? +
diff --git a/src/app/project/list/list-item-form/list-item-form.component.spec.ts b/src/app/project/list/list-item-form/list-item-form.component.spec.ts index 62e1288153..6657da62c0 100644 --- a/src/app/project/list/list-item-form/list-item-form.component.spec.ts +++ b/src/app/project/list/list-item-form/list-item-form.component.spec.ts @@ -1,69 +1,181 @@ -import { HttpClientModule } from '@angular/common/http'; +import { OverlayContainer } from '@angular/cdk/overlay'; +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MatDialogHarness } from '@angular/material/dialog/testing'; import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterTestingModule } from '@angular/router/testing'; -import { KnoraApiConnection } from '@dasch-swiss/dsp-js'; +import { ApiResponseData, DeleteListNodeResponse, ListsEndpointAdmin, StringLiteral } from '@dasch-swiss/dsp-js'; import { - AppInitService, DspActionModule, - DspApiConfigToken, - DspApiConnectionToken, - DspCoreModule + DspApiConnectionToken } from '@dasch-swiss/dsp-ui'; import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { AjaxResponse } from 'rxjs/ajax'; +import { DialogHeaderComponent } from 'src/app/main/dialog/dialog-header/dialog-header.component'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; -import { ErrorComponent } from 'src/app/main/error/error.component'; -import { TestConfig } from 'test.config'; -import { ListItemFormComponent } from './list-item-form.component'; +import { ListItemFormComponent, ListNodeOperation } from './list-item-form.component'; + +/** + * Test host component to simulate parent component. + */ +@Component({ + template: ` + + ` +}) +class TestHostComponent implements OnInit { + + @ViewChild('listItemForm') listItemForm: ListItemFormComponent; + + iri = 'http://rdfh.ch/lists/0001/notUsedList01'; + + language = 'en'; + + projectIri = 'http://rdfh.ch/projects/0001'; + + projectCode = '0001'; + + labels: StringLiteral[]; + + constructor() {} + + ngOnInit() { + this.labels = [ + { + value: 'node 1', + language: 'en' + } + ]; + } + +} describe('ListItemFormComponent', () => { - let component: ListItemFormComponent; - let fixture: ComponentFixture; + let testHostComponent: TestHostComponent; + let testHostFixture: ComponentFixture; + let rootLoader: HarnessLoader; + let overlayContainer: OverlayContainer; beforeEach(async(() => { + + const listsEndpointSpyObj = { + admin: { + listsEndpoint: jasmine.createSpyObj('listsEndpoint', ['deleteListNode']) + } + }; + TestBed.configureTestingModule({ declarations: [ ListItemFormComponent, + TestHostComponent, DialogComponent, - ErrorComponent + DialogHeaderComponent ], imports: [ BrowserAnimationsModule, DspActionModule, - DspCoreModule, - HttpClientModule, MatIconModule, - MatInputModule, - ReactiveFormsModule, - RouterTestingModule, + MatDialogModule, + MatButtonModule, TranslateModule.forRoot() ], providers: [ - AppInitService, { - provide: DspApiConfigToken, - useValue: TestConfig.ApiConfig + provide: DspApiConnectionToken, + useValue: listsEndpointSpyObj }, { - provide: DspApiConnectionToken, - useValue: new KnoraApiConnection(TestConfig.ApiConfig) - } + provide: MAT_DIALOG_DATA, + useValue: {} + }, + { + provide: MatDialogRef, + useValue: {} + }, ] - }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(ListItemFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + testHostFixture.detectChanges(); + + expect(testHostComponent).toBeTruthy(); + + testHostComponent.listItemForm.showActionBubble = true; + testHostFixture.detectChanges(); + + overlayContainer = TestBed.inject(OverlayContainer); + rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostFixture); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(async () => { + const dialogs = await rootLoader.getAllHarnesses(MatDialogHarness); + await Promise.all(dialogs.map(async d => await d.close())); + + // angular won't call this for us so we need to do it ourselves to avoid leaks. + overlayContainer.ngOnDestroy(); + }); + + it('should show a dialog box when the delete button is clicked', async () => { + + const deleteListNodeResponse: DeleteListNodeResponse = new DeleteListNodeResponse(); + deleteListNodeResponse.node.children = []; + deleteListNodeResponse.node.id = 'http://rdfh.ch/lists/0001/notUsedList'; + deleteListNodeResponse.node.isRootNode = true; + deleteListNodeResponse.node.name = 'notUsedList'; + deleteListNodeResponse.node.projectIri = 'http://rdfh.ch/projects/0001'; + + const listSpy = TestBed.inject(DspApiConnectionToken); + + (listSpy.admin.listsEndpoint as jasmine.SpyObj).deleteListNode.and.callFake( + () => { + + const response = deleteListNodeResponse; + + return of(ApiResponseData.fromAjaxResponse({response} as AjaxResponse)); + } + ); + + spyOn(testHostComponent.listItemForm.refreshParent, 'emit'); + + const deleteButton = await rootLoader.getHarness(MatButtonHarness.with({selector: '.delete'})); + await deleteButton.click(); + + const dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness); + + expect(dialogHarnesses.length).toEqual(1); + + const confirmButton = await rootLoader.getHarness(MatButtonHarness.with({selector: '.confirm-button'})); + + await confirmButton.click(); + + const listNodeOperation: ListNodeOperation = new ListNodeOperation(); + listNodeOperation.listNode = deleteListNodeResponse.node; + listNodeOperation.operation = 'delete'; + + testHostFixture.whenStable().then(() => { + expect(listSpy.admin.listsEndpoint.deleteListNode).toHaveBeenCalledWith(testHostComponent.iri); + expect(listSpy.admin.listsEndpoint.deleteListNode).toHaveBeenCalledTimes(1); + + expect(testHostComponent.listItemForm.refreshParent.emit).toHaveBeenCalledWith(listNodeOperation); + expect(testHostComponent.listItemForm.refreshParent.emit).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/app/project/list/list-item-form/list-item-form.component.ts b/src/app/project/list/list-item-form/list-item-form.component.ts index dcca6ec66d..3edfbf6824 100644 --- a/src/app/project/list/list-item-form/list-item-form.component.ts +++ b/src/app/project/list/list-item-form/list-item-form.component.ts @@ -1,15 +1,15 @@ -import { trigger, state, style, transition, animate } from '@angular/animations'; +import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { ApiResponseData, ApiResponseError, ChildNodeInfo, CreateChildNodeRequest, + DeleteListNodeResponse, KnoraApiConnection, ListInfoResponse, - ListNodeInfo, + ListNode, ListNodeInfoResponse, StringLiteral } from '@dasch-swiss/dsp-js'; @@ -17,6 +17,11 @@ 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'; + listNode: ListNode; +} + @Component({ selector: 'app-list-item-form', templateUrl: './list-item-form.component.html', @@ -71,7 +76,7 @@ export class ListItemFormComponent implements OnInit { // set main / pre-defined language @Input() language?: string; - @Output() refreshParent: EventEmitter = new EventEmitter(); + @Output() refreshParent: EventEmitter = new EventEmitter(); loading: boolean; @@ -127,25 +132,36 @@ export class ListItemFormComponent implements OnInit { this.loading = true; // generate the data payload - const listItem: CreateChildNodeRequest = new CreateChildNodeRequest(); - listItem.parentNodeIri = this.parentIri; - listItem.projectIri = this.projectIri; - listItem.name = this.projectcode + '-' + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2); + const childNode: CreateChildNodeRequest = new CreateChildNodeRequest(); + childNode.parentNodeIri = this.parentIri; + childNode.projectIri = this.projectIri; + childNode.name = this.projectcode + '-' + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2); // initialize labels let i = 0; for (const l of this.labels) { - listItem.labels[i] = new StringLiteral(); - listItem.labels[i].language = l.language; - listItem.labels[i].value = l.value; + childNode.labels[i] = new StringLiteral(); + childNode.labels[i].language = l.language; + childNode.labels[i].value = l.value; i++; } - listItem.comments = []; // TODO: comments are not yet implemented in the template + childNode.comments = []; // TODO: comments are not yet implemented in the template + + // init data to emit to parent + const listNodeOperation: ListNodeOperation = new ListNodeOperation(); // send payload to dsp-api's api - this._dspApiConnection.admin.listsEndpoint.createChildNode(listItem).subscribe( + this._dspApiConnection.admin.listsEndpoint.createChildNode(childNode).subscribe( (response: ApiResponseData) => { - this.refreshParent.emit(response.body.nodeinfo); + // this needs to return a ListNode as opposed to a ListNodeInfo, so we make one + listNodeOperation.listNode = new ListNode(); + listNodeOperation.listNode.hasRootNode = response.body.nodeinfo.hasRootNode; + listNodeOperation.listNode.id = response.body.nodeinfo.id; + listNodeOperation.listNode.labels = response.body.nodeinfo.labels; + listNodeOperation.listNode.name = response.body.nodeinfo.name; + listNodeOperation.listNode.position = response.body.nodeinfo.position; + listNodeOperation.operation = 'create'; + this.refreshParent.emit(listNodeOperation); this.loading = false; }, (error: ApiResponseError) => { @@ -182,7 +198,7 @@ export class ListItemFormComponent implements OnInit { } /** - * Called when the 'edit' button is clicked. + * Called when the 'edit' or 'delete' button is clicked. * * @param mode mode to tell DialogComponent which part of the template to show. * @param name label of the node; for now this is always the first label in the array. @@ -203,12 +219,49 @@ export class ListItemFormComponent implements OnInit { dialogConfig ); - dialogRef.afterClosed().subscribe((data: ChildNodeInfo) => { - // update the view if data was passed back - // data is only passed back when clicking the 'update' button - if (data) { - this.refreshParent.emit(data as ListNodeInfo); + dialogRef.afterClosed().subscribe((data: ChildNodeInfo | boolean) => { + + // init data to emit to parent + const listNodeOperation = new ListNodeOperation(); + + if (data instanceof ChildNodeInfo) { // 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 + // delete the node + this._dspApiConnection.admin.listsEndpoint.deleteListNode(iri).subscribe( + (response: ApiResponseData) => { + listNodeOperation.listNode = response.body.node; + listNodeOperation.operation = 'delete'; + + // emit data to parent to update the view + this.refreshParent.emit(listNodeOperation); + }, + (error: ApiResponseError) => { + // if DSP-API returns a 400, it is likely that the list node is in use so we inform the user of this + if (error.status === 400) { + const errorDialogConfig: MatDialogConfig = { + width: '640px', + position: { + top: '112px' + }, + data: { mode: 'deleteListNodeError'} + }; + + // open the dialog box + this._dialog.open(DialogComponent, errorDialogConfig); + } else { + // use default error behavior + this._errorHandler.showMessage(error); + } + + } + ); } }); } diff --git a/src/app/project/list/list-item/list-item.component.html b/src/app/project/list/list-item/list-item.component.html index 0a9153a4f1..5149cede05 100644 --- a/src/app/project/list/list-item/list-item.component.html +++ b/src/app/project/list/list-item/list-item.component.html @@ -17,7 +17,7 @@ + [projectIri]="projectIri" [projectcode]="projectcode" (refreshChildren)="updateParentNodeChildren($event, node.position)"> diff --git a/src/app/project/list/list-item/list-item.component.spec.ts b/src/app/project/list/list-item/list-item.component.spec.ts index ce3575e03b..c563ffcf44 100644 --- a/src/app/project/list/list-item/list-item.component.spec.ts +++ b/src/app/project/list/list-item/list-item.component.spec.ts @@ -1,66 +1,204 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, resolveForwardRef, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterTestingModule } from '@angular/router/testing'; -import { KnoraApiConnection } from '@dasch-swiss/dsp-js'; +import { ApiResponseData, ListNode, ListNodeInfo, ListResponse, ListsEndpointAdmin, StringLiteral } from '@dasch-swiss/dsp-js'; import { - AppInitService, DspActionModule, - DspApiConfigToken, DspApiConnectionToken } from '@dasch-swiss/dsp-ui'; +import { of } from 'rxjs'; +import { AjaxResponse } from 'rxjs/ajax'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; import { ErrorComponent } from 'src/app/main/error/error.component'; -import { TestConfig } from 'test.config'; -import { ListItemFormComponent } from '../list-item-form/list-item-form.component'; +import { ListItemFormComponent, ListNodeOperation } from '../list-item-form/list-item-form.component'; import { ListItemComponent } from './list-item.component'; +/** + * Test host component to simulate parent component. + */ +@Component({ + template: ` + + ` +}) +class TestHostComponent implements OnInit { + + @ViewChild('listItem') listItem: ListItemComponent; + + list: ListNodeInfo[]; + + parentIri = 'http://rdfh.ch/lists/0001/otherTreeList'; + + projectIri = 'http://rdfh.ch/projects/0001'; + + projectCode = '0001'; + + constructor() {} + + ngOnInit() { + this.list = [ + { + comments: [], + id: 'http://rdfh.ch/lists/0001/otherTreeList', + labels: [{value: 'Tree List Node', language: 'en'}], + isRootNode: true + } + ]; + } + +} + +/** + * Mock ListItemForm. + */ +@Component({ + template: `` +}) +class MockListItemFormComponent { } + describe('ListItemComponent', () => { - let component: ListItemComponent; - let fixture: ComponentFixture; + let testHostComponent: TestHostComponent; + let testHostFixture: ComponentFixture; beforeEach(async(() => { + + const listsEndpointSpyObj = { + admin: { + listsEndpoint: jasmine.createSpyObj('listsEndpoint', ['getList']) + } + }; + TestBed.configureTestingModule({ declarations: [ ListItemComponent, - ListItemFormComponent, - DialogComponent, - ErrorComponent + MockListItemFormComponent, + TestHostComponent ], imports: [ BrowserAnimationsModule, DspActionModule, - HttpClientTestingModule, - MatIconModule, - MatInputModule, - ReactiveFormsModule, - RouterTestingModule + MatIconModule ], providers: [ - AppInitService, - { - provide: DspApiConfigToken, - useValue: TestConfig.ApiConfig - }, { provide: DspApiConnectionToken, - useValue: new KnoraApiConnection(TestConfig.ApiConfig) + useValue: listsEndpointSpyObj } - ] + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(ListItemComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + + const dspConnSpy = TestBed.inject(DspApiConnectionToken); + + (dspConnSpy.admin.listsEndpoint as jasmine.SpyObj).getList.and.callFake( + () => { + const response = new ListResponse(); + response.list.listinfo.id = 'http://rdfh.ch/lists/0001/otherTreeList'; + response.list.listinfo.isRootNode = true; + response.list.listinfo.labels = [{value: 'Tree List Node Root', language: 'en'}]; + response.list.children = [ + { + comments: [], + labels: [{value: 'Tree List Node 01', language: 'en'}], + id: 'http://rdfh.ch/lists/0001/otherTreeList01', + children: [ + { + comments: [], + labels: [{value: 'Tree List Node 03', language: 'en'}], + id: 'http://rdfh.ch/lists/0001/otherTreeList03', + children: [] + } + ] + }, + { + comments: [], + labels: [{value: 'Tree List Node 02', language: 'en'}], + id: 'http://rdfh.ch/lists/0001/otherTreeList02', + children: [] + } + ]; + return of(ApiResponseData.fromAjaxResponse({response} as AjaxResponse)); + } + ); + + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + testHostFixture.detectChanges(); + + expect(testHostComponent).toBeTruthy(); + expect(testHostComponent.listItem.list.length).toEqual(2); + }); + + it('should update the view to show a newly created node', () => { + const listNodeOperation: ListNodeOperation = new ListNodeOperation(); + listNodeOperation.listNode = { + children: [], + comments: [], + hasRootNode: 'http://rdfh.ch/lists/0001/otherTreeList', + id: 'http://rdfh.ch/lists/0001/otherTreeList04', + labels: [{value: 'Tree List Node 04', language: 'en'}] + }; + listNodeOperation.operation = 'create'; + + testHostComponent.listItem.updateView(listNodeOperation); + + expect(testHostComponent.listItem.list.length).toEqual(3); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should update the view to show an updated node', () => { + const listNodeOperation: ListNodeOperation = new ListNodeOperation(); + listNodeOperation.listNode = { + children: undefined, + comments: [], + hasRootNode: 'http://rdfh.ch/lists/0001/otherTreeList', + id: 'http://rdfh.ch/lists/0001/otherTreeList01', + labels: [{value: 'Tree List Node 0123', language: 'en'}], + position: 0 + }; + listNodeOperation.operation = 'update'; + + testHostComponent.listItem.updateView(listNodeOperation); + + expect(testHostComponent.listItem.list.length).toEqual(2); + + expect(testHostComponent.listItem.list[0].labels).toEqual([{value: 'Tree List Node 0123', language: 'en'}]); + }); + + it('should update the view to remove a deleted node', () => { + const listNodeOperation: ListNodeOperation = new ListNodeOperation(); + listNodeOperation.listNode = { + children: [ + { + comments: [], + labels: [{value: 'Tree List Node 02', language: 'en'}], + id: 'http://rdfh.ch/lists/0001/otherTreeList02', + children: [] + } + ], + comments: [], + isRootNode: true, + id: 'http://rdfh.ch/lists/0001/otherTreeList01', + labels: [{value: 'Tree List Root', language: 'en'}], + projectIri: 'http://rdfh.ch/projects/0001' + }; + listNodeOperation.operation = 'delete'; + + testHostComponent.listItem.updateView(listNodeOperation); + + expect(testHostComponent.listItem.list.length).toEqual(1); + + expect(testHostComponent.listItem.list[0].labels).toEqual([{value: 'Tree List Node 02', language: 'en'}]); }); }); diff --git a/src/app/project/list/list-item/list-item.component.ts b/src/app/project/list/list-item/list-item.component.ts index 850d97f6d2..4fae0f1e96 100644 --- a/src/app/project/list/list-item/list-item.component.ts +++ b/src/app/project/list/list-item/list-item.component.ts @@ -1,14 +1,14 @@ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; import { ApiResponseData, ApiResponseError, - ChildNodeInfo, KnoraApiConnection, ListNode, ListResponse } from '@dasch-swiss/dsp-js'; import { DspApiConnectionToken } from '@dasch-swiss/dsp-ui'; import { ErrorHandlerService } from 'src/app/main/error/error-handler.service'; +import { ListNodeOperation } from '../list-item-form/list-item-form.component'; @Component({ selector: 'app-list-item', @@ -29,6 +29,8 @@ export class ListItemComponent implements OnInit { @Input() language?: string; + @Output() refreshChildren: EventEmitter = new EventEmitter(); + expandedNode: string; constructor( @@ -37,8 +39,7 @@ export class ListItemComponent implements OnInit { ) { } ngOnInit() { - - // in case of parent node: do not run the following request + // in case of parent node: run the following request to get the entire list if (!this.childNode) { this._dspApiConnection.admin.listsEndpoint.getList(this.parentIri).subscribe( (result: ApiResponseData) => { @@ -66,7 +67,6 @@ export class ListItemComponent implements OnInit { * @param id id of parent node for which the 'expand' button was clicked. */ toggleChildren(id: string) { - if (this.showChildren(id)) { this.expandedNode = undefined; } else { @@ -78,28 +78,54 @@ export class ListItemComponent implements OnInit { /** * Called when the 'refreshParent' event from ListItemFormComponent is triggered. * - * @param data info about the node; can be a root node or child node. + * @param data info about the operation that was performed on the node and should be reflected in the UI. * @param firstNode states whether or not the node is a new child node; defaults to false. */ - updateView(data: ListNode, firstNode: boolean = false) { - - if (data instanceof ChildNodeInfo) { - this.list[data.position].labels = data.labels; - this.list[data.position].comments = data.comments; - } else { - // update the view by updating the existing list - if (firstNode) { - // in case of new child node, we have to use the children from list - const index: number = this.list.findIndex(item => item.id === this.expandedNode); - this.list[index].children.push(data); - - } else { - this.list.push(data); + updateView(data: ListNodeOperation, firstNode: boolean = false) { + // update the view by updating the existing list + if (data instanceof ListNodeOperation) { + switch (data.operation) { + case 'create': { + if (firstNode) { + // in case of new child node, we have to use the children from list + const index: number = this.list.findIndex(item => item.id === this.expandedNode); + this.list[index].children.push(data.listNode); + + } else { + this.list.push(data.listNode); + } + break; + } + case 'update': { + // use the position from the response from DSP-API to find the correct node to update + this.list[data.listNode.position].labels = data.listNode.labels; + this.list[data.listNode.position].comments = data.listNode.comments; + break; + } + case 'delete': { + // conveniently, the response returned by DSP-API contains the entire list without the deleted node within its 'children' + // we can just reassign the list to this + this.list = data.listNode.children; + + // emit the updated list of children to the parent node + this.refreshChildren.emit(this.list); + break; + } + default: { + break; + } } - - data.children = []; } + } + /** + * updates the children of the parent node + * + * @param children the updated list of children nodes + * @param position the position of the parent node + */ + updateParentNodeChildren(children: ListNode[], position: number) { + this.list[position].children = children; } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index aa4e39ecb2..9db6fc45b0 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -151,7 +151,8 @@ }, "lists": { "title": { - "delete": "Delete list" + "deleteList": "Delete list", + "deleteListNode": "Delete list node" } }, "resource": {