diff --git a/package-lock.json b/package-lock.json
index 5fbd990f6b..6af25c3b9e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -69,6 +69,7 @@
"@babel/core": "^7.18.13",
"@compodoc/compodoc": "^1.1.19",
"@cypress/schematic": "~2.0.3",
+ "@oasisdigital/angular-typed-forms-helpers": "^1.3.2",
"@schematics/angular": "^14.2.0",
"@storybook/addon-actions": "^6.5.10",
"@storybook/addon-essentials": "^6.5.10",
@@ -4732,6 +4733,15 @@
"tao": "index.js"
}
},
+ "node_modules/@oasisdigital/angular-typed-forms-helpers": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@oasisdigital/angular-typed-forms-helpers/-/angular-typed-forms-helpers-1.3.2.tgz",
+ "integrity": "sha512-dLATZYh+mspxdYz9UQRXfsctSMlHNjmxUxOoHXYrdxXm7kgSXEbv/gpogtSuwQg26WQM0lNRtixoLvqO7LNwWg==",
+ "dev": true,
+ "dependencies": {
+ "@angular/forms": "^14.0.1"
+ }
+ },
"node_modules/@parcel/watcher": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz",
@@ -36217,6 +36227,15 @@
"nx": "14.5.10"
}
},
+ "@oasisdigital/angular-typed-forms-helpers": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@oasisdigital/angular-typed-forms-helpers/-/angular-typed-forms-helpers-1.3.2.tgz",
+ "integrity": "sha512-dLATZYh+mspxdYz9UQRXfsctSMlHNjmxUxOoHXYrdxXm7kgSXEbv/gpogtSuwQg26WQM0lNRtixoLvqO7LNwWg==",
+ "dev": true,
+ "requires": {
+ "@angular/forms": "^14.0.1"
+ }
+ },
"@parcel/watcher": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz",
@@ -36641,7 +36660,7 @@
"ts-loader": "^8.0.14",
"tsconfig-paths-webpack-plugin": "^3.3.0",
"util-deprecate": "^1.0.2",
- "webpack": ">=4.0.0 <6.0.0"
+ "webpack": "5.74.0"
},
"dependencies": {
"@types/react": {
@@ -36852,7 +36871,7 @@
"ts-dedent": "^2.0.0",
"url-loader": "^4.1.1",
"util-deprecate": "^1.0.2",
- "webpack": "4",
+ "webpack": "5.74.0",
"webpack-dev-middleware": "^3.7.3",
"webpack-filter-warnings-plugin": "^1.2.1",
"webpack-hot-middleware": "^2.25.1",
@@ -37587,7 +37606,7 @@
"terser-webpack-plugin": "^5.0.3",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
- "webpack": "^5.9.0",
+ "webpack": "5.74.0",
"webpack-dev-middleware": "^4.1.0",
"webpack-hot-middleware": "^2.25.1",
"webpack-virtual-modules": "^0.4.1"
@@ -38007,7 +38026,7 @@
"telejson": "^6.0.8",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
- "webpack": "4"
+ "webpack": "5.74.0"
},
"dependencies": {
"@babel/helper-define-polyfill-provider": {
@@ -38267,7 +38286,7 @@
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
"watchpack": "^2.2.0",
- "webpack": "4",
+ "webpack": "5.74.0",
"ws": "^8.2.3",
"x-default-browser": "^0.4.0"
},
@@ -38452,7 +38471,7 @@
"ts-dedent": "^2.0.0",
"url-loader": "^4.1.1",
"util-deprecate": "^1.0.2",
- "webpack": "4",
+ "webpack": "5.74.0",
"webpack-dev-middleware": "^3.7.3",
"webpack-virtual-modules": "^0.2.2"
},
@@ -39199,7 +39218,7 @@
"terser-webpack-plugin": "^5.0.3",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
- "webpack": "^5.9.0",
+ "webpack": "5.74.0",
"webpack-dev-middleware": "^4.1.0",
"webpack-virtual-modules": "^0.4.1"
},
diff --git a/package.json b/package.json
index 2585e111e6..57a8b30654 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,7 @@
"@babel/core": "^7.18.13",
"@compodoc/compodoc": "^1.1.19",
"@cypress/schematic": "~2.0.3",
+ "@oasisdigital/angular-typed-forms-helpers": "^1.3.2",
"@schematics/angular": "^14.2.0",
"@storybook/addon-actions": "^6.5.10",
"@storybook/addon-essentials": "^6.5.10",
diff --git a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html
index 28006ef5e1..77947a66cb 100644
--- a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html
+++ b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html
@@ -1,9 +1,21 @@
-
Edit Progress
+
+
+
+ This field is required
+
+
-
+
-
+
diff --git a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.scss b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.scss
index d348085cf6..b80d92adcf 100644
--- a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.scss
+++ b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.scss
@@ -18,6 +18,14 @@
margin-bottom: 12px;
}
+.title-field {
+ input{
+ font-size: 32px;
+ }
+ width: 100%;
+ min-width: 0;
+}
+
.header-field {
grid-area: header;
min-width: 0;
diff --git a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.spec.ts b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.spec.ts
index 70c0ef40c5..97ab2d2f71 100644
--- a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.spec.ts
+++ b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.spec.ts
@@ -5,15 +5,14 @@ import {
EditProgressDashboardComponentData,
} from "./edit-progress-dashboard.component";
import { MAT_DIALOG_DATA } from "@angular/material/dialog";
-import { ReactiveFormsModule, FormBuilder } from "@angular/forms";
-import { TypedForm } from "../../../core/entity-components/entity-form/entity-form.service";
-import { ProgressDashboardPart } from "../progress-dashboard/progress-dashboard-config";
+import { FormBuilder, FormGroup, ReactiveFormsModule } from "@angular/forms";
describe("EditProgressDashboardComponent", () => {
let component: EditProgressDashboardComponent;
let fixture: ComponentFixture
;
const mockDialogData: EditProgressDashboardComponentData = {
+ title: "qwe",
parts: [
{
label: "foo",
@@ -47,8 +46,8 @@ describe("EditProgressDashboardComponent", () => {
}).compileComponents();
});
- function getGroup(index: number): TypedForm {
- return component.forms.at(index);
+ function getGroup(index: number): FormGroup {
+ return component.parts.at(index) as FormGroup;
}
beforeEach(() => {
@@ -62,19 +61,30 @@ describe("EditProgressDashboardComponent", () => {
});
it("should contain the initial state from the data", () => {
- expect(component.forms).toHaveValue(mockDialogData.parts);
- expect(component.forms).toBeValidForm();
+ expect(component.parts).toHaveValue(mockDialogData.parts);
+ expect(component.parts).toBeValidForm();
+
+ expect(component.title).toHaveValue(mockDialogData.title);
+ expect(component.title).toBeValidForm();
+ });
+
+ it("should mark form as invalid when title is empty", () => {
+ component.title.setValue("");
+ expect(component.title).toHaveValue("");
+ expect(component.title).toContainFormError("required");
+
+ expect(component.title).not.toBeValidForm();
});
it("should append a new part", () => {
component.addPart();
- expect(component.forms).toHaveSize(4);
+ expect(component.parts).toHaveSize(4);
});
it("should delete a part", () => {
component.removePart(1);
- expect(component.forms).toHaveSize(2);
- expect(component.forms).toHaveValue([
+ expect(component.parts).toHaveSize(2);
+ expect(component.parts).toHaveValue([
mockDialogData.parts[0],
mockDialogData.parts[2],
]);
@@ -82,9 +92,9 @@ describe("EditProgressDashboardComponent", () => {
it("should mark the form as invalid when current or target is not present", () => {
const firstForm = getGroup(0);
- firstForm.get("currentValue").setValue(undefined);
+ firstForm.get("currentValue").setValue("");
expect(firstForm.get("currentValue")).toContainFormError("required");
- firstForm.get("targetValue").setValue(undefined);
+ firstForm.get("targetValue").setValue("");
expect(firstForm.get("targetValue")).toContainFormError("required");
expect(firstForm).not.toBeValidForm();
diff --git a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts
index 15bb0528a4..eb56f9ed63 100644
--- a/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts
+++ b/src/app/features/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts
@@ -1,18 +1,21 @@
import { Component, Inject } from "@angular/core";
import { MAT_DIALOG_DATA } from "@angular/material/dialog";
-import { ProgressDashboardPart } from "../progress-dashboard/progress-dashboard-config";
import {
- UntypedFormControl,
- FormGroupDirective,
- NgForm,
+ ProgressDashboardPart,
+ ProgressDashboardConfig,
+} from "../progress-dashboard/progress-dashboard-config";
+import {
+ FormBuilder,
+ FormControl,
+ FormGroup,
ValidationErrors,
Validators,
- FormBuilder,
} from "@angular/forms";
import { ErrorStateMatcher } from "@angular/material/core";
-import { TypedForm } from "../../../core/entity-components/entity-form/entity-form.service";
+import { AngularForm } from "@oasisdigital/angular-typed-forms-helpers";
export interface EditProgressDashboardComponentData {
+ title: string;
parts: ProgressDashboardPart[];
}
@@ -22,18 +25,31 @@ export interface EditProgressDashboardComponentData {
styleUrls: ["./edit-progress-dashboard.component.scss"],
})
export class EditProgressDashboardComponent {
- forms = this.fb.array(this.data.parts.map((part) => this.formGroup(part)));
- currentErrorStateMatcher = new FormCurrentErrorStateMatcher();
+ /**
+ * This marks the control as invalid when the whole form has an error
+ */
+ readonly currentErrorStateMatcher: ErrorStateMatcher = {
+ isErrorState: (control: FormControl | null) => !control?.parent?.valid,
+ };
+
+ title = new FormControl(this.data.title, [Validators.required]);
+ parts = this.fb.array(
+ this.data.parts.map((part) => this.createPartForm(part))
+ );
+ outputData = new FormGroup({
+ title: this.title,
+ parts: this.parts,
+ });
constructor(
- @Inject(MAT_DIALOG_DATA) public data: EditProgressDashboardComponentData,
+ @Inject(MAT_DIALOG_DATA) private data: ProgressDashboardConfig,
private fb: FormBuilder
) {}
- formGroup(part: ProgressDashboardPart) {
+ createPartForm(part: ProgressDashboardPart) {
return this.fb.group(
{
- label: this.fb.control(part.label),
+ label: this.fb.control(part.label, [Validators.required]),
currentValue: this.fb.control(part.currentValue, [
Validators.required,
Validators.min(0),
@@ -50,7 +66,7 @@ export class EditProgressDashboardComponent {
}
currentLessThanTarget(
- control: TypedForm
+ control: AngularForm
): ValidationErrors | null {
const current = control.get("currentValue");
const target = control.get("targetValue");
@@ -69,25 +85,10 @@ export class EditProgressDashboardComponent {
currentValue: 1,
targetValue: 10,
};
- this.forms.push(this.formGroup(newPart));
- }
-
- get tooltipOnSave(): string {
- return this.forms.valid
- ? ""
- : $localize`:Shown when there are errors that prevent saving:Fix the errors to save the form`;
+ this.parts.push(this.createPartForm(newPart));
}
removePart(index: number) {
- this.forms.removeAt(index);
- }
-}
-
-class FormCurrentErrorStateMatcher implements ErrorStateMatcher {
- isErrorState(
- control: UntypedFormControl | null,
- form: FormGroupDirective | NgForm | null
- ): boolean {
- return !control?.parent?.valid;
+ this.parts.removeAt(index);
}
}
diff --git a/src/app/features/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts b/src/app/features/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts
index 3a4aba1253..73daeb41db 100644
--- a/src/app/features/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts
+++ b/src/app/features/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts
@@ -71,7 +71,7 @@ export class ProgressDashboardComponent
.afterClosed()
.subscribe(async (next) => {
if (next) {
- this.data.parts = next;
+ Object.assign(this.data, next);
await this.save();
}
});