From d0e475adfb55e5538164b16d169e0b9a4e6be702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 4 Aug 2021 08:56:30 +0200 Subject: [PATCH] feat(workspace): add intermediate view (DSP-1834) (#494) * feat(workspace): add intermediate view (DSP-1834) * test(workspace): test intermediate view * refactor(workspace): clean up code * test(workspace): bug fix in tests --- src/app/app.module.ts | 2 + .../intermediate/intermediate.component.html | 41 ++++ .../intermediate/intermediate.component.scss | 75 ++++++++ .../intermediate.component.spec.ts | 178 ++++++++++++++++++ .../intermediate/intermediate.component.ts | 27 +++ .../workspace/results/results.component.html | 17 +- .../workspace/results/results.component.ts | 12 +- 7 files changed, 342 insertions(+), 10 deletions(-) create mode 100644 src/app/workspace/intermediate/intermediate.component.html create mode 100644 src/app/workspace/intermediate/intermediate.component.scss create mode 100644 src/app/workspace/intermediate/intermediate.component.spec.ts create mode 100644 src/app/workspace/intermediate/intermediate.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0dfec737cb..a9e0dcbf83 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -96,6 +96,7 @@ import { SelectResourceClassComponent } from './workspace/resource/resource-inst import { ResourceComponent } from './workspace/resource/resource.component'; import { ResultsComponent } from './workspace/results/results.component'; import { AudioComponent } from './workspace/resource/representation/audio/audio.component'; +import { IntermediateComponent } from './workspace/intermediate/intermediate.component'; // translate: AoT requires an exported function for factories export function httpLoaderFactory(httpClient: HttpClient) { @@ -178,6 +179,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { UsersListComponent, VisualizerComponent, AudioComponent, + IntermediateComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/workspace/intermediate/intermediate.component.html b/src/app/workspace/intermediate/intermediate.component.html new file mode 100644 index 0000000000..41c3e6293f --- /dev/null +++ b/src/app/workspace/intermediate/intermediate.component.html @@ -0,0 +1,41 @@ +
+
+
+
+
+

{{resources.count}}

+

{{resources.count | i18nPlural: itemPluralMapping['resource']}} Selected

+
+
+ + + + + + + + + + + +
+
+
+
+
+
diff --git a/src/app/workspace/intermediate/intermediate.component.scss b/src/app/workspace/intermediate/intermediate.component.scss new file mode 100644 index 0000000000..676d409567 --- /dev/null +++ b/src/app/workspace/intermediate/intermediate.component.scss @@ -0,0 +1,75 @@ +@import '../../../assets/style/config'; +@import '../../../assets/style/mixins'; + +.card-container { + position: relative; + width: 18em; + margin: 4em auto; +} + +.card { + height: 20.5em; + width: 20em; + position: absolute; + border-radius: $border-radius; + background: #cecece; + padding: 32px 16px; + box-shadow: 1px 1px 7px rgba(0,0,0,.65); + + &.front { + z-index: 10; + } + + &.background { + z-index: -1; + + &.two { + transform: rotateZ(4deg); + } + + &.three { + transform: rotateZ(-4deg); + } + + &.more { + transform: rotateZ(-8deg); + } + + } + + .mock { + background: rgba(0,0,0,.375); + min-height: 1em; + color: $primary_100; + + &.title { + width: 70%; + margin: 8px 0; + } + + &.content { + height: 14em; + margin-top: -3em; + text-align: center; + + .count { + font-size: 4em; + line-height:0.75em; + margin-bottom:0; + padding-top: 72px; + } + } + } + + .action { + height: 2em; + display: inline-flex; + margin: 1em 0; + width: 100%; + + button { + margin: 0 16px; + } + + } +} diff --git a/src/app/workspace/intermediate/intermediate.component.spec.ts b/src/app/workspace/intermediate/intermediate.component.spec.ts new file mode 100644 index 0000000000..f1a9dd8f04 --- /dev/null +++ b/src/app/workspace/intermediate/intermediate.component.spec.ts @@ -0,0 +1,178 @@ +import { Component, DebugElement, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { By } from '@angular/platform-browser'; +import { FilteredResouces } from '@dasch-swiss/dsp-ui'; +import { IntermediateComponent } from './intermediate.component'; + +/** + * test host component to simulate parent component + */ +@Component({ + template: '' +}) +class OneSelectedResourcesComponent { + + @ViewChild('intermediateView') intermediateComponent: IntermediateComponent; + + resources: FilteredResouces = { + 'count': 1, + 'resListIndex': [1], + 'resIds': [ + 'http://rdfh.ch/0803/83616f8d8501' + ], + 'selectionType': 'multiple' + }; + + constructor() { } + +} + +/** + * test host component to simulate parent component + */ +@Component({ + template: '' +}) +class ThreeSelectedResourcesComponent { + + @ViewChild('intermediateView') intermediateComponent: IntermediateComponent; + + resources: FilteredResouces = { + 'count': 3, + 'resListIndex': [3, 2, 1], + 'resIds': [ + 'http://rdfh.ch/0803/83616f8d8501', + 'http://rdfh.ch/0803/71e0b9958a01', + 'http://rdfh.ch/0803/683d5cd26f01' + ], + 'selectionType': 'multiple' + }; + + constructor() { } + +} + +describe('IntermediateComponent', () => { + + let host1Component: OneSelectedResourcesComponent; + let host1Fixture: ComponentFixture; + + let host3Component: ThreeSelectedResourcesComponent; + let host3Fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + IntermediateComponent, + OneSelectedResourcesComponent, + ThreeSelectedResourcesComponent + ], + imports: [ + MatButtonModule, + MatIconModule, + MatTooltipModule + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + host1Fixture = TestBed.createComponent(OneSelectedResourcesComponent); + host1Component = host1Fixture.componentInstance; + host1Fixture.detectChanges(); + + expect(host1Component).toBeTruthy(); + + host3Fixture = TestBed.createComponent(ThreeSelectedResourcesComponent); + host3Component = host3Fixture.componentInstance; + host3Fixture.detectChanges(); + + expect(host3Component).toBeTruthy(); + + }); + + describe('One selected resource', () => { + + it('expect count to be "1" and text to be "Resource Selected', () => { + expect(host1Component.intermediateComponent).toBeTruthy(); + expect(host1Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host1Fixture.debugElement; + + const count: DebugElement = hostCompDe.query(By.css('.count')); + expect(count.nativeElement.innerText).toEqual('1'); + const text: DebugElement = hostCompDe.query(By.css('.text')); + expect(text.nativeElement.innerText).toEqual('Resource Selected'); + + }); + + it('expect compare button to be disabled', () => { + expect(host1Component.intermediateComponent).toBeTruthy(); + expect(host1Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host1Fixture.debugElement; + + const button: DebugElement = hostCompDe.query(By.css('.compare')); + expect(button.nativeElement.disabled).toBeTruthy(); + }); + + it('expect no card stack', () => { + expect(host1Component.intermediateComponent).toBeTruthy(); + expect(host1Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host1Fixture.debugElement; + + const twoCards: DebugElement = hostCompDe.query(By.css('.two')); + expect(twoCards).toBeFalsy(); + const threeCards: DebugElement = hostCompDe.query(By.css('.three')); + expect(threeCards).toBeFalsy(); + const moreCards: DebugElement = hostCompDe.query(By.css('.more')); + expect(moreCards).toBeFalsy(); + }); + + }); + + describe('Three selected resources', () => { + + it('expect count to be "3" and text to be "Resources Selected', () => { + expect(host3Component.intermediateComponent).toBeTruthy(); + expect(host3Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host3Fixture.debugElement; + + const count: DebugElement = hostCompDe.query(By.css('.count')); + expect(count.nativeElement.innerText).toEqual('3'); + const text: DebugElement = hostCompDe.query(By.css('.text')); + expect(text.nativeElement.innerText).toEqual('Resources Selected'); + + }); + + it('expect compare button to be enabled', () => { + expect(host3Component.intermediateComponent).toBeTruthy(); + expect(host3Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host3Fixture.debugElement; + + const button: DebugElement = hostCompDe.query(By.css('.compare')); + expect(button.nativeElement.disabled).toBeFalsy(); + }); + + it('expect card stack of more than 2', () => { + expect(host3Component.intermediateComponent).toBeTruthy(); + expect(host3Component.intermediateComponent.resources).toBeDefined(); + + const hostCompDe = host3Fixture.debugElement; + + const twoCards: DebugElement = hostCompDe.query(By.css('.two')); + expect(twoCards.nativeElement).toBeDefined(); + const threeCards: DebugElement = hostCompDe.query(By.css('.three')); + expect(threeCards.nativeElement).toBeDefined(); + const moreCards: DebugElement = hostCompDe.query(By.css('.more')); + expect(moreCards).toBeFalsy(); + }); + }); + +}); diff --git a/src/app/workspace/intermediate/intermediate.component.ts b/src/app/workspace/intermediate/intermediate.component.ts new file mode 100644 index 0000000000..195b6ad9d7 --- /dev/null +++ b/src/app/workspace/intermediate/intermediate.component.ts @@ -0,0 +1,27 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FilteredResouces } from '@dasch-swiss/dsp-ui'; + +@Component({ + selector: 'app-intermediate', + templateUrl: './intermediate.component.html', + styleUrls: ['./intermediate.component.scss'] +}) +export class IntermediateComponent implements OnInit { + + @Input() resources: FilteredResouces; + + @Output() action: EventEmitter = new EventEmitter(); + + // i18n plural mapping + itemPluralMapping = { + resource: { + '=1': 'Resource', + other: 'Resources' + } + }; + + constructor() { } + + ngOnInit(): void { } + +} diff --git a/src/app/workspace/results/results.component.html b/src/app/workspace/results/results.component.html index ac32b9f231..1d8d66dc90 100644 --- a/src/app/workspace/results/results.component.html +++ b/src/app/workspace/results/results.component.html @@ -10,13 +10,18 @@ - - +
+ + - - - + + + + + + +
diff --git a/src/app/workspace/results/results.component.ts b/src/app/workspace/results/results.component.ts index 21172a09d2..933d053472 100644 --- a/src/app/workspace/results/results.component.ts +++ b/src/app/workspace/results/results.component.ts @@ -17,8 +17,8 @@ export class ResultsComponent { resourceIri: string; // display single resource or intermediate page in case of multiple selection - multipleSelection = false; multipleResources: FilteredResouces; + viewMode: 'single' | 'intermediate' | 'compare' = 'single'; // number of all results numberOfAllResults: number; @@ -54,7 +54,7 @@ export class ResultsComponent { } openResource(id: string) { - this.multipleSelection = false; + this.viewMode = 'single'; this.multipleResources = undefined; this.resourceIri = id; } @@ -63,9 +63,13 @@ export class ResultsComponent { // multiple resources are selected for comparision openMultipleResources(resources: FilteredResouces) { - this.multipleSelection = (resources && resources.count > 0); + if (this.viewMode !== 'compare') { + + this.viewMode = ((resources && resources.count > 0) ? 'intermediate' : 'single'); + + this.multipleResources = (this.viewMode !== 'single' ? resources : undefined); + } - this.multipleResources = (this.multipleSelection ? resources : undefined); }