Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(action): migrate action module (DSP-1852) #509

Merged
merged 11 commits into from Aug 19, 2021
10 changes: 7 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions src/app/app.module.ts
Expand Up @@ -98,6 +98,22 @@ import { ResultsComponent } from './workspace/results/results.component';
import { AudioComponent } from './workspace/resource/representation/audio/audio.component';
import { IntermediateComponent } from './workspace/intermediate/intermediate.component';
import { ResourceLinkFormComponent } from './workspace/resource/resource-link-form/resource-link-form.component';
import { ConfirmationDialogComponent } from './main/action/confirmation-dialog/confirmation-dialog.component';
import { ConfirmationMessageComponent } from './main/action/confirmation-dialog/confirmation-message/confirmation-message.component';
import { LoginFormComponent } from './main/action/login-form/login-form.component';
import { MessageComponent } from './main/action/message/message.component';
import { ProgressIndicatorComponent } from './main/action/progress-indicator/progress-indicator.component';
import { StringLiteralInputComponent } from './main/action/string-literal-input/string-literal-input.component';
import { SortButtonComponent } from './main/action/sort-button/sort-button.component';
import { SelectedResourcesComponent } from './main/action/selected-resources/selected-resources.component';
import { AdminImageDirective } from './main/directive/admin-image/admin-image.directive';
import { ExistingNameDirective } from './main/directive/existing-name/existing-name.directive';
import { GndDirective } from './main/directive/gnd/gnd.directive';
import { FormattedBooleanPipe } from './main/pipes/formatting/formatted-boolean.pipe';
import { KnoraDatePipe } from './main/pipes/formatting/knoradate.pipe';
import { LinkifyPipe } from './main/pipes/string-transformation/linkify.pipe';
import { StringifyStringLiteralPipe } from './main/pipes/string-transformation/stringify-string-literal.pipe';
import { TruncatePipe } from './main/pipes/string-transformation/truncate.pipe';

// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
Expand Down Expand Up @@ -182,6 +198,22 @@ export function httpLoaderFactory(httpClient: HttpClient) {
AudioComponent,
IntermediateComponent,
ResourceLinkFormComponent,
ConfirmationDialogComponent,
ConfirmationMessageComponent,
LoginFormComponent,
MessageComponent,
ProgressIndicatorComponent,
StringLiteralInputComponent,
SortButtonComponent,
SelectedResourcesComponent,
AdminImageDirective,
ExistingNameDirective,
GndDirective,
FormattedBooleanPipe,
KnoraDatePipe,
LinkifyPipe,
StringifyStringLiteralPipe,
TruncatePipe,
],
imports: [
AppRoutingModule,
Expand Down
@@ -0,0 +1,11 @@
<div class="deletion-dialog">
<mat-dialog-content>
<p class="title"> Are you sure you want to delete this value from {{data.value.propertyLabel}}?</p>
<app-confirmation-message #confirmMessage [value]="data.value"></app-confirmation-message>
</mat-dialog-content>
<mat-dialog-actions class="action-buttons">
<button class="cancel" mat-raised-button mat-dialog-close>{{data.buttonTextCancel}}</button>
<button class="ok" mat-raised-button color="primary" (click)="onConfirmClick()">{{data.buttonTextOk}}</button>
</mat-dialog-actions>
</div>

@@ -0,0 +1 @@
@import "../../../../assets/style/viewer";
@@ -0,0 +1,168 @@
import { OverlayContainer } from '@angular/cdk/overlay';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { Component, Input, OnInit } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MockResource, ReadIntValue, ReadValue } from '@dasch-swiss/dsp-js';
import { ConfirmationDialogComponent, ConfirmationDialogValueDeletionPayload } from './confirmation-dialog.component';

/**
* test host component to simulate parent component with a confirmation dialog.
*/
@Component({
template: `
<p> {{confirmationDialogResponse}} </p>`
})
class ConfirmationDialogTestHostComponent implements OnInit {

confirmationDialogResponse: string;

testValue: ReadIntValue;

constructor(private dialog: MatDialog) {
}

ngOnInit() {
MockResource.getTestThing().subscribe(res => {
this.testValue = res.getValuesAs('http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger', ReadIntValue)[0];
});
}

openDialog() {

this.dialog.open(ConfirmationDialogComponent, {
data: {
value: this.testValue,
buttonTextOk: 'OK',
buttonTextCancel: 'Cancel'
}
}).afterClosed().subscribe((payload: ConfirmationDialogValueDeletionPayload) => {
if (payload.confirmed) {
this.confirmationDialogResponse = 'Action was confirmed!';
} else {
this.confirmationDialogResponse = 'Action was cancelled';
}
});
}
}

@Component({ selector: 'app-confirmation-message', template: '' })
class MockConfirmationMessageComponent {
@Input() value: ReadValue;

constructor() { }
}

describe('ConfirmationDialogComponent', () => {
let testHostComponent: ConfirmationDialogTestHostComponent;
let testHostFixture: ComponentFixture<ConfirmationDialogTestHostComponent>;
let rootLoader: HarnessLoader;
let overlayContainer: OverlayContainer;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [
ConfirmationDialogComponent,
ConfirmationDialogTestHostComponent,
MockConfirmationMessageComponent
],
imports: [
MatDialogModule,
BrowserAnimationsModule
],
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: {}
},
{
provide: MatDialogRef,
useValue: {}
}
]
})
.compileComponents();

overlayContainer = TestBed.inject(OverlayContainer);
}));

beforeEach(() => {
testHostFixture = TestBed.createComponent(ConfirmationDialogTestHostComponent);
testHostComponent = testHostFixture.componentInstance;
rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostFixture);
testHostFixture.detectChanges();
expect(testHostComponent).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 display a confirmation dialog', async () => {

testHostComponent.openDialog();

testHostFixture.detectChanges();

await testHostFixture.whenStable();

const dialogDiv = document.querySelector('mat-dialog-container');
expect(dialogDiv).toBeTruthy();

const dspConfirmMsg = document.querySelector('app-confirmation-message');
expect(dspConfirmMsg).toBeTruthy();

const dialogTitle = dialogDiv.querySelector('.title');
expect(dialogTitle.innerHTML.trim()).toEqual('Are you sure you want to delete this value from Integer?');

});

it('should return a confirmation message when the OK button is clicked', async () => {

testHostComponent.openDialog();

let dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness);

expect(dialogHarnesses.length).toEqual(1);

const okButton = await rootLoader.getHarness(MatButtonHarness.with({ selector: '.ok' }));

await okButton.click();

dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness);

expect(dialogHarnesses.length).toEqual(0);

expect(testHostComponent.confirmationDialogResponse).toEqual('Action was confirmed!');

});

it('should return a cancelled message when the cancel button is clicked', async () => {

testHostComponent.openDialog();

let dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness);

expect(dialogHarnesses.length).toEqual(1);

const cancelButton = await rootLoader.getHarness(MatButtonHarness.with({ selector: '.cancel' }));

await cancelButton.click();

dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness);

expect(dialogHarnesses.length).toEqual(0);

expect(testHostComponent.confirmationDialogResponse).toEqual('Action was cancelled');


});
});
@@ -0,0 +1,39 @@
import { Component, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ReadValue } from '@dasch-swiss/dsp-js';
import { ConfirmationMessageComponent } from './confirmation-message/confirmation-message.component';

export class ConfirmationDialogData {
value: ReadValue;
buttonTextOk: string;
buttonTextCancel: string;
}

export class ConfirmationDialogValueDeletionPayload {
confirmed: boolean;
deletionComment?: string;
}

@Component({
selector: 'app-confirmation-dialog',
templateUrl: './confirmation-dialog.component.html',
styleUrls: ['./confirmation-dialog.component.scss']
})
export class ConfirmationDialogComponent {
@ViewChild('confirmMessage') confirmationMessageComponent: ConfirmationMessageComponent;

// type assertion doesn't seem to be enforced
// https://stackoverflow.com/a/57787554
constructor(
@Inject(MAT_DIALOG_DATA) public data: ConfirmationDialogData,
private _dialogRef: MatDialogRef<ConfirmationDialogComponent>
) { }

onConfirmClick(): void {
const payload = new ConfirmationDialogValueDeletionPayload();
payload.confirmed = true;
payload.deletionComment = this.confirmationMessageComponent.comment ? this.confirmationMessageComponent.comment : undefined;
this._dialogRef.close(payload);
}

}
@@ -0,0 +1,13 @@
<div class="deletion-dialog-message">
<p class="val-label">Confirming this action will delete the following value from {{value.propertyLabel}}:</p>
<p class="val-value">Value: {{value.strval}}</p>
<p class="val-comment">Value Comment: {{value.valueHasComment ? value.valueHasComment : 'No comment'}}</p>
<p class="val-creation-date">Value Creation Date: {{value.valueCreationDate}}</p>
<textarea
matinput
class="deletion-comment"
type="text"
(keyup)="onKey($event)"
[placeholder]="'Comment why value is being deleted'"
></textarea>
</div>
@@ -0,0 +1 @@
@import "../../../../../assets/style/viewer";