Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ import { AboutUsComponent } from './component/about-us/about-us.component';
import { DependencyGraphComponent } from './component/dependency-graph/dependency-graph.component';
import { TeamsComponent } from './component/teams/teams.component';
import { ToStringValuePipe } from './pipe/to-string-value.pipe';
import { ModalMessageComponent } from './component/modal-message/modal-message.component';
import {
MatDialogModule,
MAT_DIALOG_DATA,
MatDialogRef,
} from '@angular/material/dialog';

@NgModule({
declarations: [
Expand All @@ -40,16 +46,23 @@ import { ToStringValuePipe } from './pipe/to-string-value.pipe';
TeamsComponent,
ToStringValuePipe,
UserdayComponent,
ModalMessageComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MaterialModule,
MatDialogModule,
ReactiveFormsModule,
HttpClientModule,
],
providers: [ymlService],
providers: [
ymlService,
ModalMessageComponent,
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: MatDialogRef, useValue: { close: (dialogResult: any) => {} } },
],
bootstrap: [AppComponent],
})
export class AppModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ <h2>Nothing to show</h2>
class="normal-button"
mat-raised-button
class="resetButtonClass"
(click)="ResetIsImplemented()">
Reset Implemented
(click)="deleteLocalTeamsProgress()">
Delete team progress
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,25 @@ import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service';
import { CircularHeatmapComponent } from './circular-heatmap.component';
import { RouterTestingModule } from '@angular/router/testing';
import { MatChip } from '@angular/material/chips';
import { ModalMessageComponent } from '../modal-message/modal-message.component';

describe('CircularHeatmapComponent', () => {
let component: CircularHeatmapComponent;
let fixture: ComponentFixture<CircularHeatmapComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
providers: [ymlService, HttpClient, HttpHandler],
declarations: [CircularHeatmapComponent, MatChip],
imports: [RouterTestingModule],
declarations: [CircularHeatmapComponent],
providers: [
ymlService,
HttpClient,
HttpHandler,
{ provide: ModalMessageComponent, useValue: {} },
],
}).compileComponents();
});
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [MatChip],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(CircularHeatmapComponent);
fixture = TestBed.createComponent(CircularHeatmapComponent); // Create fixture and component here
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down
37 changes: 33 additions & 4 deletions src/app/component/circular-heatmap/circular-heatmap.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import * as yaml from 'js-yaml';
import { Router } from '@angular/router';
import { MatChip } from '@angular/material/chips';
import * as md from 'markdown-it';
import {
ModalMessageComponent,
DialogInfo,
} from '../modal-message/modal-message.component';

export interface activitySchema {
uuid: string;
Expand Down Expand Up @@ -62,7 +66,7 @@ export class CircularHeatmapComponent implements OnInit {
constructor(
private yaml: ymlService,
private router: Router,
private changeDetector: ChangeDetectorRef
public modal: ModalMessageComponent
) {
this.showOverlay = false;
}
Expand All @@ -83,6 +87,14 @@ export class CircularHeatmapComponent implements OnInit {
@ViewChildren(MatChip) chips!: QueryList<MatChip>;
matChipsArray: MatChip[] = [];

displayMessage(dialogInfo: DialogInfo) {
// Remove focus from the button that becomes aria unavailable (avoids ugly console error message)
const buttonElement = document.activeElement as HTMLElement;
buttonElement.blur();

this.modal.openDialog(dialogInfo);
}

private LoadMaturityDataFromGeneratedYaml() {
return new Promise<void>((resolve, reject) => {
console.log(`${this.perfNow()}s: LoadMaturityData Fetch`);
Expand Down Expand Up @@ -843,9 +855,26 @@ export class CircularHeatmapComponent implements OnInit {
this.noActivitytoGrey();
}

ResetIsImplemented() {
localStorage.removeItem('dataset');
this.loadDataset();
deleteLocalTeamsProgress() {
// Remove focus from the button that becomes aria unavailable (avoids ugly console error message)
const buttonElement = document.activeElement as HTMLElement;
buttonElement.blur();

let title: string = 'Delete local browser data';
let message: string =
'Do you want to delete all progress for each team?' +
'\n\nThis deletes all progress stored in your local browser, but does ' +
'not change any progress stored in the yaml file on the server.';
let buttons: string[] = ['Cancel', 'Delete'];
this.modal
.openDialog({ title, message, buttons, template: '' })
.afterClosed()
.subscribe(data => {
if (data === 'Delete') {
localStorage.removeItem('dataset');
location.reload(); // Make sure all load routines are initialized
}
});
}

saveDataset() {
Expand Down
14 changes: 14 additions & 0 deletions src/app/component/modal-message/modal-message.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.dialog {
margin: 0.5em;
padding: 1em;
}

.dialog-buttons {
display: flex;
justify-content: flex-end;
}

button {
min-width: 5rem;
margin: 0 1rem;
}
14 changes: 14 additions & 0 deletions src/app/component/modal-message/modal-message.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div mat-dialog-content class="dialog">
<h2 mat-dialog-title>{{ data.title }}</h2>
<p [innerHTML]="data.message"></p>
</div>
<div mat-dialog-actions class="dialog-buttons">
<button
*ngFor="let name of data.buttons"
mat-button
mat-dialog-close
class="mat-focus-indicator downloadButtonClass mat-raised-button mat-button-base"
(click)="closeDialog(name)">
{{ name }}
</button>
</div>
52 changes: 52 additions & 0 deletions src/app/component/modal-message/modal-message.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DialogInfo, ModalMessageComponent } from './modal-message.component';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatDialogModule } from '@angular/material/dialog';

describe('ModalMessageComponent', () => {
let component: ModalMessageComponent;
let fixture: ComponentFixture<ModalMessageComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NoopAnimationsModule, MatDialogModule],
declarations: [ModalMessageComponent],
providers: [
{ provide: MatDialogRef, useValue: {} },
{ provide: MAT_DIALOG_DATA, useValue: {} },
],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(ModalMessageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should render markdown correctly in the dialog', () => {
const dialogInfo: DialogInfo = new DialogInfo('A **test** markdown.');
const dialogRef: MatDialogRef<ModalMessageComponent> =
component.openDialog(dialogInfo);

expect(dialogRef.componentInstance.data.message).toContain(
'<strong>test</strong>'
);
});

it('should render markdown correctly in the dialog', () => {
const dialogInfo: DialogInfo = new DialogInfo('A **test** markdown.');
const dialogRef: MatDialogRef<ModalMessageComponent> =
component.openDialog(dialogInfo);

// Check if markdown rendering is applied
expect(dialogRef.componentInstance.data.message).toContain(
'<strong>test</strong>'
);
});
});
87 changes: 87 additions & 0 deletions src/app/component/modal-message/modal-message.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Inject, Component, OnInit } from '@angular/core';
import {
MAT_DIALOG_DATA,
MatDialogRef,
MatDialog,
MatDialogConfig,
} from '@angular/material/dialog';
import * as md from 'markdown-it';

@Component({
selector: 'app-modal-message',
templateUrl: './modal-message.component.html',
styleUrls: ['./modal-message.component.css'],
})
export class ModalMessageComponent implements OnInit {
data: DialogInfo;
markdown: md = md();

DSOMM_host: string = 'https://github.com/devsecopsmaturitymodel';
DSOMM_url: string = `${this.DSOMM_host}/DevSecOps-MaturityModel-data`;
meassageTemplates: Record<string, DialogInfo> = {
generated_yaml: new DialogInfo(
`{message}\n\n` +
`Please download the activity template \`generated.yaml\` ` +
`from [DSOMM-data](${this.DSOMM_url}) on GitHub.\n\n` +
'The DSOMM activities are maintained and distributed ' +
'separately from the software.',
'DSOMM startup problems'
),
};

constructor(
public dialog: MatDialog,
public dialogRef: MatDialogRef<ModalMessageComponent>,
@Inject(MAT_DIALOG_DATA) data: DialogInfo
) {
this.data = data;
}

// eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method
ngOnInit(): void {}

openDialog(
dialogInfo: DialogInfo | string
): MatDialogRef<ModalMessageComponent> {
if (typeof dialogInfo === 'string') {
dialogInfo = new DialogInfo(dialogInfo);
}
if (
dialogInfo.template &&
this.meassageTemplates.hasOwnProperty(dialogInfo.template)
) {
let template: DialogInfo = this.meassageTemplates[dialogInfo.template];
dialogInfo.title = dialogInfo.title || template?.title;
dialogInfo.message = template?.message?.replace(
'{message}',
dialogInfo.message
);
}

dialogInfo.message = this.markdown.render(dialogInfo.message);

const dialogConfig = new MatDialogConfig();
dialogConfig.id = 'modal-message';
dialogConfig.disableClose = true;
dialogConfig.data = dialogInfo;
dialogConfig.autoFocus = false;
this.dialogRef = this.dialog.open(ModalMessageComponent, dialogConfig);
return this.dialogRef;
}

closeDialog(buttonName: string) {
this.dialogRef?.close(buttonName);
}
}

export class DialogInfo {
title: string = '';
template: string | null = '';
message: string = '';
buttons: string[] = ['OK'];

constructor(msg: string = '', title: string = '') {
this.message = msg;
this.title = title;
}
}