Skip to content
Permalink
Browse files
feat(list-value): show hierarchy of list node (DEV-1092) (#805)
* feat(list-value): show hierarchy of list node

* chore(list-value): remove test class
  • Loading branch information
mdelez committed Aug 24, 2022
1 parent afa1a84 commit 26e88c300badaf539c2e3baaac61d8134480dea0
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 26 deletions.
@@ -1,8 +1,11 @@
<span *ngIf="mode !== 'read' && listRootNode !== undefined">

<!-- button to select a list node -->
<span [matTooltip]="(!selectedNode && !listRootNode.children.length) ? 'This list doesn\'t have any node values' : 'Select list value'" [matTooltipPosition]="'above'">
<button mat-stroked-button [matMenuTriggerFor]="mainMenu" type="button" placeholder="List value" [disabled]="!selectedNode && !listRootNode.children.length">
<span
[matTooltip]="(!selectedNode && !listRootNode.children.length) ? 'This list doesn\'t have any node values' : 'Select list value'"
[matTooltipPosition]="'above'">
<button mat-stroked-button [matMenuTriggerFor]="mainMenu" type="button" placeholder="List value"
[disabled]="!selectedNode && !listRootNode.children.length">

<!-- if no node is selected: display list label; if the list does not have any list nodes, the button should be disabled -->
<span *ngIf="!selectedNode">{{listRootNode.label}}</span>
@@ -17,7 +20,7 @@
<span *ngFor="let child of listRootNode.children">
<span *ngIf="child.children && child.children.length > 0">
<button mat-menu-item [matMenuTriggerFor]="menu.childMenu" (click)="getSelectedNode(child)"
type="button">
type="button">
{{child.label}}
</button>
<app-sublist-value #menu [children]="child.children" (selectedNode)="getSelectedNode($event)">
@@ -36,29 +39,28 @@
<span class="custom-error-message">New value must be different than the current value.</span>
</mat-error>
<mat-error *ngIf="valueFormControl.hasError('duplicateValue')">
<span class="custom-error-message">This value already exists for this property. Duplicate values are not allowed.</span>
<span class="custom-error-message">This value already exists for this property. Duplicate values are not
allowed.</span>
</mat-error>
</span>
<span *ngIf="mode === 'read'; else showForm" class="read-mode-view">
<span class="rm-value">{{valueFormControl.value}}</span>
<span class="rm-value list">
<span class="hierarchy" *ngFor="let item of selectedNodeHierarchy, let first = first, let last = last">
<mat-icon *ngIf="!first">chevron_right</mat-icon>
<span [class.last]="last">{{ item }}</span>
</span>
</span>
<span class="rm-comment" *ngIf="shouldShowComment">{{commentFormControl.value}}</span>
</span>
<ng-template #showForm>
<span [formGroup]="form">
<mat-form-field class="large-field child-value-component" *ngIf="mode === 'read'" floatLabel="never">
<mat-form-field class="large-field child-value-component" *ngIf="mode === 'read'" floatLabel="never">
<input [formControlName]="'value'" class="value" type="text" placeholder="List value" matInput readonly>
</mat-form-field>
<mat-form-field class="large-field value-component-comment">
<textarea matInput
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cdkAutosizeMaxRows="6"
[formControlName]="'comment'"
class="comment"
placeholder="Comment"
type="text"
spellcheck="false">
</mat-form-field>
<mat-form-field class="large-field value-component-comment">
<textarea matInput cdkTextareaAutosize cdkAutosizeMinRows="1" cdkAutosizeMaxRows="6"
[formControlName]="'comment'" class="comment" placeholder="Comment" type="text" spellcheck="false">
</textarea>
</mat-form-field>
</mat-form-field>
</span>
</ng-template>
</ng-template>
@@ -5,6 +5,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
@@ -40,7 +41,7 @@ class TestHostDisplayValueComponent implements OnInit {

MockResource.getTestThing().subscribe(res => {
const inputVal: ReadListValue =
res.getValuesAs('http://0.0.0.0:3333/ontology/0001/anything/v2#hasListItem', ReadListValue)[0];
res.getValuesAs('http://0.0.0.0:3333/ontology/0001/anything/v2#hasListItem', ReadListValue)[0];
this.displayInputVal = inputVal;
this.mode = 'read';
});
@@ -92,6 +93,7 @@ describe('ListValueComponent', () => {
MatInputModule,
MatMenuModule,
MatSnackBarModule,
MatTooltipModule,
ReactiveFormsModule,
],
providers: [
@@ -114,6 +116,18 @@ describe('ListValueComponent', () => {
let commentInputNativeElement;

beforeEach(() => {
const valuesSpy = TestBed.inject(DspApiConnectionToken);
(valuesSpy.v2.list as jasmine.SpyObj<ListsEndpointV2>).getList.and.callFake(
(rootNodeIri: string) => {
const res = new ListNodeV2();
res.id = 'http://rdfh.ch/lists/0001/treeList01';
res.label = 'Tree list node 01';
res.isRootNode = false;
res.children = [];
return of(res);
}
);

testHostFixture = TestBed.createComponent(TestHostDisplayValueComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();
@@ -158,7 +172,7 @@ describe('ListValueComponent', () => {

expect(testHostComponent.inputValueComponent.mode).toEqual('update');

expect(valuesSpy.v2.list.getList).toHaveBeenCalledTimes(1);
expect(valuesSpy.v2.list.getList).toHaveBeenCalledTimes(3);
expect(valuesSpy.v2.list.getList).toHaveBeenCalledWith('http://rdfh.ch/lists/0001/treeList');
expect(testHostComponent.inputValueComponent.listRootNode.children.length).toEqual(0);

@@ -14,7 +14,6 @@ import { Subscription } from 'rxjs';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { BaseValueDirective } from 'src/app/main/directive/base-value.directive';
import { ErrorHandlerService } from 'src/app/main/services/error-handler.service';
import { NotificationService } from 'src/app/main/services/notification.service';

// https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror
const resolvedPromise = Promise.resolve(null);
@@ -42,6 +41,8 @@ export class ListValueComponent extends BaseValueDirective implements OnInit, On

customValidators = [];

selectedNodeHierarchy: string[] = [];

constructor(
@Inject(FormBuilder) private _fb: FormBuilder,
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
@@ -52,6 +53,7 @@ export class ListValueComponent extends BaseValueDirective implements OnInit, On

getInitValue(): string | null {
if (this.displayValue !== undefined) {
this.getReadModeValue(this.displayValue.listNode);
return this.displayValue.listNode;
} else {
return null;
@@ -160,4 +162,40 @@ export class ListValueComponent extends BaseValueDirective implements OnInit, On
this.valueFormControl.setValue(item.id);
}

getReadModeValue(nodeIri: string): void {
const rootNodeIris = this.propertyDef.guiAttributes;
for (const rootNodeIri of rootNodeIris) {
const trimmedRootNodeIRI = rootNodeIri.substr(7, rootNodeIri.length - (1 + 7));
this._dspApiConnection.v2.list.getList(trimmedRootNodeIRI).subscribe(
(response: ListNodeV2) => {
if (!response.children.length) { // this shouldn't happen since users cannot select the root node
this.selectedNodeHierarchy.push(response.label);
} else {
this.selectedNodeHierarchy = this._getHierarchy(nodeIri, response.children);
}
}, (error: ApiResponseError) => {
this._errorHandler.showMessage(error);
});
}
}

_getHierarchy(selectedNodeIri: string, children: ListNodeV2[]): string[] {
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (node.id !== selectedNodeIri) {
if (node.children) {
const path = this._getHierarchy(selectedNodeIri, node.children);

if (path) {
path.unshift(node.label);
return path;
}
}
} else {
return [node.label];
}
}
}


}
@@ -2,14 +2,25 @@

.read-mode-view {
font: 400 15px/24px sans-serif;
.rm-value, .rm-comment {

.rm-value,
.rm-comment {
display: block;
margin: 0px;
}

.rm-value.text-value {
white-space: pre-wrap;
}

.rm-value.list,
.rm-value .hierarchy {
display: inline-flex;

.last {
font-weight: bold;
}
}
}

.child-value-component,
@@ -36,6 +47,7 @@
display: inline-block;
vertical-align: bottom;
width: 49%;

&:nth-child(2) {
padding-left: 2%;
}
@@ -91,6 +103,7 @@
}

.value-action {

.material-icons,
.mat-icon {
font-size: 18px;
@@ -139,9 +152,10 @@
background-color: inherit !important;
}

code, pre {
code,
pre {
font-family: monospace !important;
background-color: hsla(0,0%,78%,.3);
background-color: hsla(0, 0%, 78%, .3);
padding: .15em;
border-radius: 2px;
}
}

0 comments on commit 26e88c3

Please sign in to comment.