Skip to content

Commit

Permalink
feat(viewer): add checkbox for every resource in list and grid view (D…
Browse files Browse the repository at this point in the history
…SP-1711) (#311)

* feat(resource viewer): add checkbox for every resouce

* feat(resource viewer): add function to list and count selected resources

* feat(resource viewer): add functionality to grid view

* feat(resource viewer): update testcases

* feat(resource viewer): add interface for filtered resources

* feat(resource viewer): display checkbox at right side in grid view

* feat(resource viewer): add two cases - to select single resource or multiple resources

* feat(resource viewer): create separate component resource-list-content

* feat(resource viewer): update component resource-list-content

* feat(resource viewer): create separate component resource-grid-content

* feat(resource viewer): cleanup

* feat(resource viewer): cleanup, highlight selected resource(s)

* feat(resource viewer): update tests

* feat(resource viewer): added deprecated warning for resourceSelected event

* feat(resource viewer): update multiple selection case

- when withMultipleSelection is true and clicked on the resource, it will go
  to the resource details page
- when withMultipleSelection is true and checkbox(s) is selected, it will go to
  the resource comparison page

- resource-grid-content component is deleted
- resource-list-content component is deleted

* feat(resource viewer): small changes
  • Loading branch information
Snehal Kumbhar committed Jul 9, 2021
1 parent 74f1cb9 commit 1e0381a
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 96 deletions.
Expand Up @@ -21,25 +21,46 @@
<!-- container with search results -->
<div class="list-view-container">
<dsp-progress-indicator *ngIf="loading"></dsp-progress-indicator>

<div class="list-view" *ngIf="!loading && numberOfAllResults > 0 && resources">
<!-- When single resources is selected to view -->
<div class="list-view" *ngIf="!loading && numberOfAllResults > 0 && resources && withMultipleSelection === false">
<div [ngSwitch]="view">
<dsp-resource-list
*ngSwitchCase="'list'"
[resources]="resources"
[selectedResourceIdx]="selectedResourceIdx"
(resourceSelected)="emitSelectedResource($event)">
(resourcesSelected)="emitSelectedResources($event)">
</dsp-resource-list>
<dsp-resource-grid
*ngSwitchCase="'grid'"
[resources]="resources"
[selectedResourceIdx]="selectedResourceIdx"
(resourceSelected)="emitSelectedResource($event)">
(resourcesSelected)="emitSelectedResources($event)">
</dsp-resource-grid>
<!-- TODO: implement table view -->
<!-- <kui-table-view *ngSwitchCase="'table'" [resources]="resources"></kui-table-view> -->
</div>
</div>

<!-- When multiple resources are selected for comparision -->
<div class="list-view" *ngIf="!loading && numberOfAllResults > 0 && resources && withMultipleSelection">
<div [ngSwitch]="view">
<dsp-resource-list
*ngSwitchCase="'list'"
[withMultipleSelection]="true"
[resources]="resources"
[selectedResourceIdx]="selectedResourceIdx"
(resourcesSelected)="emitSelectedResources($event)">
</dsp-resource-list>
<dsp-resource-grid
*ngSwitchCase="'grid'"
[withMultipleSelection]="true"
[resources]="resources"
[selectedResourceIdx]="selectedResourceIdx"
(resourcesSelected)="emitSelectedResources($event)">
</dsp-resource-grid>
<!-- TODO: implement table view -->
<!-- <kui-table-view *ngSwitchCase="'table'" [resources]="resources"></kui-table-view> -->
</div>
</div>

<!-- In case of 0 result -->
Expand Down
Expand Up @@ -40,6 +40,6 @@ button.active {
background-color: $black-12-opacity;
}

.selected-resource {
::ng-deep .selected-resource {
background-color: $black-20-opacity;
}
Expand Up @@ -22,6 +22,30 @@ export interface SearchParams {
filter?: IFulltextSearchParams;
}

/* return the selected resources in below format
*
* count: total number of resources selected
* selectedIds: list of selected resource's ids
*/
export interface FilteredResouces {
count: number,
resListIndex: number[],
resIds: string[],
selectionType: "multiple" | "single"
}

/* return the checkbox value
*
* checked: checkbox value
* resIndex: resource index from the list
*/
export interface checkboxUpdate {
checked: boolean,
resListIndex: number,
resId: string,
isCheckbox: boolean
}

@Component({
selector: 'dsp-list-view',
templateUrl: './list-view.component.html',
Expand All @@ -36,15 +60,33 @@ export class ListViewComponent implements OnChanges {
@Input() displayViewSwitch?: boolean = true;

/**
* Click on an item will emit the resource iri
* Set to true if multiple resources can be selected for comparison
*/
@Input() withMultipleSelection?: boolean = false;

/**
* Click on checkbox will emit the resource info
*
* @param {EventEmitter<string>} resourceSelected
* @param {EventEmitter<FilteredResouces>} resourcesSelected
*/
@Output() multipleResourcesSelected?: EventEmitter<FilteredResouces> = new EventEmitter<FilteredResouces>();

/**
* @deprecated Use singleResourceSelected instead.
* Click on an item will emit the resource iri
*/
@Output() resourceSelected: EventEmitter<string> = new EventEmitter<string>();

/**
* Click on an item will emit the resource iri
*
* @param {EventEmitter<string>} singleResourceSelected
*/
@Output() singleResourceSelected?: EventEmitter<string> = new EventEmitter<string>();

resources: ReadResourceSequence;

selectedResourceIdx = 0;
selectedResourceIdx: number[] = [];

// MatPaginator Output
pageEvent: PageEvent;
Expand Down Expand Up @@ -78,15 +120,17 @@ export class ListViewComponent implements OnChanges {
this.view = view;
}

emitSelectedResource(id: string) {
// get selected resource index from list to highlight it
for (let idx = 0; idx < this.resources.resources.length; idx++) {
if (this.resources.resources[idx].id === id) {
this.selectedResourceIdx = idx;
break;
}
// If 'withMultipleSelection' is true, multiple resources are selected for comparision
// If 'withMultipleSelection' is false, single resource is selected for viewing
emitSelectedResources(resInfo: FilteredResouces) {
this.selectedResourceIdx = resInfo.resListIndex;

if (resInfo.selectionType === "multiple") {
this.multipleResourcesSelected.emit(resInfo);
} else {
this.singleResourceSelected.emit(resInfo.resIds[0]);
this.resourceSelected.emit(resInfo.resIds[0]);
}
this.resourceSelected.emit(id);
}

goToPage(page: PageEvent) {
Expand Down Expand Up @@ -117,7 +161,9 @@ export class ListViewComponent implements OnChanges {
(response: ReadResourceSequence) => {
this.resources = response;
this.loading = false;
this.emitSelectedResource(this.resources.resources[0].id);
if (!this.withMultipleSelection) {
this.emitSelectedResources({count: 1, resListIndex: [0], resIds: [this.resources.resources[0].id], selectionType: "single"});
}
},
(error: ApiResponseError) => {
this._notification.openSnackBar(error);
Expand Down Expand Up @@ -149,7 +195,9 @@ export class ListViewComponent implements OnChanges {
(response: ReadResourceSequence) => {
this.resources = response;
this.loading = false;
this.emitSelectedResource(this.resources.resources[0].id);
if (!this.withMultipleSelection) {
this.emitSelectedResources({count: 1, resListIndex: [0], resIds: [this.resources.resources[0].id], selectionType: "single"});
}
},
(error: ApiResponseError) => {
this._notification.openSnackBar(error);
Expand Down
@@ -1,24 +1,39 @@
<!-- When withMultipleSelection is false and user can select only one resource at a time to view -->
<div class="resource-grid">
<mat-card
<div
class="grid-card link"
[class.selected-resource]="selectedResourceIdx === i"
*ngFor="let res of resources.resources; let i = index;"
(click)="resourceSelected.emit(res.id)">
<!-- TODO: add the representation preview here, mat-card-image can be used for images -->
<mat-card-header class="grid-card-header">
<mat-card-subtitle class="res-class-label">{{ res.entityInfo.classes[res.type].label }}</mat-card-subtitle>
<mat-card-title class="res-class-value">{{ res.label }}</mat-card-title>
</mat-card-header>
<mat-card-content class="grid-card-content" *ngFor="let prop of res.properties | keyvalue">
<div *ngFor="let val of prop.value">
<span class="res-prop-label">
{{ res.entityInfo.properties[val.property].label }}
</span>
*ngFor="let resource of resources.resources; let i = index;">
<mat-card [class.selected-resource]="selectedResourceIdx.indexOf(i) > -1">
<!-- TODO: add the representation preview here, mat-card-image can be used for images -->
<mat-card-header class="grid-card-header">
<mat-card-title-group class="res-class-header-text" (click)="viewResource({ checked: true, resListIndex: i, resId: resource.id, isCheckbox: false})">
<mat-card-subtitle class="res-class-label">{{ resource.entityInfo.classes[resource.type].label }}</mat-card-subtitle>
<mat-card-title class="res-class-value">{{ resource.label }}</mat-card-title>
</mat-card-title-group>
<div fxFlex></div>
<!-- if withMultipleSelection is true, we display checkbox -->
<mat-card-title-group *ngIf="withMultipleSelection" class="res-class-header-actions">
<mat-checkbox #gridCkbox
id="{{ i }}"
(change)="viewResource({ checked: $event.checked, resListIndex: i, resId: resource.id, isCheckbox: true})"
class="res-checkbox">
</mat-checkbox>
</mat-card-title-group>

<div class="res-prop-value">
{{ val.strval | dspTruncate: 256:"..." }}
</div>
</mat-card-header>
<div (click)="viewResource({ checked: true, resListIndex: i, resId: resource.id, isCheckbox: false})">
<mat-card-content class="grid-card-content" *ngFor="let prop of resource.properties | keyvalue">
<div *ngFor="let val of prop.value">
<span class="res-prop-label">
{{ resource.entityInfo.properties[val.property].label }}
</span>

<div class="res-prop-value">
{{ val.strval | dspTruncate: 256:"..." }}
</div>
</div>
</mat-card-content>
</div>
</mat-card-content>
</mat-card>
</mat-card>
</div>
</div>
Expand Up @@ -16,6 +16,16 @@
height: calc(100% - 32px);
}

.res-class-header-text {
width: 90%;
float: left;
}

.res-class-header-actions {
width: 10%;
float: right;
}

.res-class-label,
.res-prop-label {
color: rgba(0, 0, 0, 0.54);
Expand All @@ -31,6 +41,11 @@
line-height: 1.5;
}

.res-class-label,
.res-class-value {
margin: 4px 0 !important; // to overwrite it with .mat-card-title margin
}

.res-prop-label {
font-style: italic;
}
Expand Down
@@ -1,8 +1,10 @@
import { Component, OnInit, Pipe, PipeTransform, ViewChild } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MockResource, ReadResourceSequence } from '@dasch-swiss/dsp-js';
import { ResourceGridComponent } from './resource-grid.component';
import { FilteredResouces } from '../list-view.component';

/**
* Mocked truncate pipe from action module.
Expand All @@ -20,15 +22,17 @@ class MockPipe implements PipeTransform {
*/
@Component({
template: `
<dsp-resource-grid #resGrid [resources]="resources" (resourceSelected)="openResource($event)"></dsp-resource-grid>`
<dsp-resource-grid #resGrid [resources]="resources" [selectedResourceIdx]="selectedResourceIdx" (resourcesSelected)="emitSelectedResources($event)"></dsp-resource-grid>`
})
class TestParentComponent implements OnInit {

@ViewChild('resGrid') resourceGridComponent: ResourceGridComponent;

resources: ReadResourceSequence;

resIri: string;
selectedResourceIdx = [0];

selectedResources: FilteredResouces;

ngOnInit() {

Expand All @@ -37,8 +41,8 @@ class TestParentComponent implements OnInit {
});
}

openResource(id: string) {
this.resIri = id;
emitSelectedResources(resInfo: FilteredResouces) {
this.selectedResources = resInfo;
}

}
Expand All @@ -55,7 +59,8 @@ describe('ResourceGridComponent', () => {
TestParentComponent
],
imports: [
MatCardModule
MatCardModule,
MatCheckboxModule
],
providers: []
})
Expand All @@ -78,14 +83,14 @@ describe('ResourceGridComponent', () => {
it('should open first resource', () => {
// trigger the click
const nativeElement = testHostFixture.nativeElement;
const item = nativeElement.querySelector('mat-card');
const item = nativeElement.querySelector('div.link');
item.dispatchEvent(new Event('click'));

spyOn(testHostComponent, 'openResource').call('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw');
expect(testHostComponent.openResource).toHaveBeenCalled();
expect(testHostComponent.openResource).toHaveBeenCalledTimes(1);
spyOn(testHostComponent, 'emitSelectedResources').call({count: 1, resListIndex: [0], resIds: ['http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'], selectionType: "single"});
expect(testHostComponent.emitSelectedResources).toHaveBeenCalled();
expect(testHostComponent.emitSelectedResources).toHaveBeenCalledTimes(1);

expect(testHostComponent.resIri).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw');
//expect(testHostComponent.resIri).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw');
});

});

0 comments on commit 1e0381a

Please sign in to comment.