This is an automated email from the ASF dual-hosted git repository.

rshah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new b96f7d34e7 Template linting for TPv2 (#7462)
b96f7d34e7 is described below

commit b96f7d34e7b7c5d9c056a6b184a3ae491417a829
Author: ocket8888 <[email protected]>
AuthorDate: Wed May 3 10:07:49 2023 -0600

    Template linting for TPv2 (#7462)
    
    * Add template linting rules
    
    * increase allowable complexity, change to warning, eliminate 
no-call-expression
    
    * fix missing button types
    
    also, some links incorrectly used `button` tags instead of `a` tags;
    fixed those too
    
    * Remove duplicate attribute
    
    * fixed unbound labels
    
    * rework complex conditional in template
    
    * Fix login button has incorrect type
    
    * Remove extraneous space
    
    * Fix e2e tests looking for button id that no longer exists
    
    * Fix button title
---
 experimental/traffic-portal/.eslintrc.json         | 51 +++++++++++++++++++++-
 .../tests/deliveryServices/ds.invalidate.spec.ts   |  4 +-
 .../asns/detail/asn-detail.component.html          |  2 +-
 .../cache-group-table.component.html               |  2 +-
 .../core/currentuser/currentuser.component.html    |  4 +-
 .../core/currentuser/currentuser.component.spec.ts | 44 +++++++++++++++++++
 .../app/core/currentuser/currentuser.component.ts  | 15 +++++++
 .../update-password-dialog.component.html          |  2 +-
 .../deliveryservice/deliveryservice.component.html |  2 +-
 .../new-invalidation-job-dialog.component.html     |  2 +-
 .../new-delivery-service.component.html            | 24 +++++-----
 .../phys-loc/table/phys-loc-table.component.html   |  2 +-
 .../server-details/server-details.component.html   |  2 +-
 .../users/user-details/user-details.component.html |  2 +-
 .../user-registration-dialog.component.html        |  2 +-
 .../src/app/core/users/users.component.html        |  2 +-
 .../src/app/login/login.component.html             |  2 +-
 .../reset-password-dialog.component.html           |  2 +-
 .../collection-choice-dialog.component.html        |  6 +--
 .../decision-dialog/decision-dialog.component.html |  4 +-
 .../dialogs/text-dialog/text-dialog.component.html |  2 +-
 .../generic-table/generic-table.component.html     |  4 +-
 .../navigation/tp-header/tp-header.component.html  | 18 ++++----
 23 files changed, 154 insertions(+), 46 deletions(-)

diff --git a/experimental/traffic-portal/.eslintrc.json 
b/experimental/traffic-portal/.eslintrc.json
index 4311f5802f..aec9f06b96 100644
--- a/experimental/traffic-portal/.eslintrc.json
+++ b/experimental/traffic-portal/.eslintrc.json
@@ -372,7 +372,56 @@
                        "extends": [
                                "plugin:@angular-eslint/template/recommended"
                        ],
-                       "rules": {}
+                       // Rules that are commented out in this block are not 
available
+                       // until Angular 15, and should be un-commented once 
that upgrade is
+                       // done.
+                       "rules": {
+                               
"@angular-eslint/template/accessibility-alt-text": "error",
+                               
"@angular-eslint/template/accessibility-elements-content": "error",
+                               // 
"@angular-eslint/template/accessibility-interactive-supports-focus": "error",
+                               
"@angular-eslint/template/accessibility-label-has-associated-control": "error",
+                               // 
"@angular-eslint/template/accessibility-role-has-required-aria": "error",
+                               
"@angular-eslint/template/accessibility-table-scope": "error",
+                               
"@angular-eslint/template/accessibility-valid-aria": "error",
+                               // I want to see the results of this before 
committing to it,
+                               // which is difficult to do until I can 
actually use it.
+                               // "@angular-eslint/template/attributes-order": 
"warn",
+                               "@angular-eslint/template/banana-in-box": 
"error",
+                               "@angular-eslint/template/button-has-type": 
"error",
+                               // Currently a warning because of suspected 
false positives.
+                               
"@angular-eslint/template/click-events-have-key-events": "warn",
+                               
"@angular-eslint/template/conditional-complexity": [
+                                       "error",
+                                       {
+                                               "maxComplexity": 2
+                                       }
+                               ],
+                               // Warning because it's difficult to know 
exactly when a
+                               // refactor is strictly necessary and also 
because fixing this
+                               // would be a huge changeset.
+                               
"@angular-eslint/template/cyclomatic-complexity": [
+                                       "warn",
+                                       {
+                                               "maxComplexity": 8
+                                       }
+                               ],
+                               "@angular-eslint/template/eqeqeq": "error",
+                               
"@angular-eslint/template/mouse-events-have-key-events": "error",
+                               "@angular-eslint/template/no-any": "error",
+                               // Currently a warning because of rampant use, 
and fixing such
+                               // usages would be out-of-scope for the PR 
adding this linting,
+                               // IMO.
+                               "@angular-eslint/template/no-autofocus": "warn",
+                               
"@angular-eslint/template/no-distracting-elements": "error",
+                               
"@angular-eslint/template/no-duplicate-attributes": "error",
+                               // "@angular-eslint/template/no-inline-styles": 
"warn",
+                               // 
"@angular-eslint/template/no-interpolation-in-attributes": "error",
+                               "@angular-eslint/template/no-negated-async": 
"error",
+                               
"@angular-eslint/template/no-positive-tabindex": "error",
+                               // Warning because not always necessary, and 
appears to have no
+                               // options to control when it is required.
+                               
"@angular-eslint/template/use-track-by-function": "warn"
+                       }
                }
        ]
 }
diff --git 
a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
 
b/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
index c7d8ff52e9..3a755dedc4 100644
--- 
a/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
+++ 
b/experimental/traffic-portal/nightwatch/tests/deliveryServices/ds.invalidate.spec.ts
@@ -38,7 +38,7 @@ describe("DS Invalidation Jobs Spec", () => {
                await 
browser.waitForElementVisible("tp-new-invalidation-job-dialog")
                        .assert.valueEquals("input[name='startDate']", 
startDate.toLocaleDateString())
                        .setValue("input[name='regexp']", "/invalidateMe")
-                       .click("button#submit");
+                       .click("button[type=submit]");
                await common
                        .assert.textContains("@snackbarEle", "created")
                        .click("simple-snack-bar button");
@@ -51,7 +51,7 @@ describe("DS Invalidation Jobs Spec", () => {
                        .assert.valueEquals("input[name='startDate']", 
startDate.toLocaleDateString())
                        .assert.valueEquals("input[name='regexp']", 
"invalidateMe")
                        .setValue("input[name='regexp']", "/invalidateMe2")
-                       .click("button#submit");
+                       .click("button[type=submit]");
                await common
                        .assert.textContains("@snackbarEle", "created")
                        .click("simple-snack-bar button");
diff --git 
a/experimental/traffic-portal/src/app/core/cache-groups/asns/detail/asn-detail.component.html
 
b/experimental/traffic-portal/src/app/core/cache-groups/asns/detail/asn-detail.component.html
index 64565c879b..e575bff0a4 100644
--- 
a/experimental/traffic-portal/src/app/core/cache-groups/asns/detail/asn-detail.component.html
+++ 
b/experimental/traffic-portal/src/app/core/cache-groups/asns/detail/asn-detail.component.html
@@ -26,7 +26,7 @@ limitations under the License.
                        </mat-form-field>
                        <mat-form-field>
                                <mat-label>Cache Group</mat-label>
-                               <mat-select 
name="cachegroup"[(ngModel)]="asn.cachegroupId" required>
+                               <mat-select name="cachegroup" 
[(ngModel)]="asn.cachegroupId" required>
                                        <mat-option *ngFor="let cachegroup of 
cachegroups" [value]="cachegroup.id">{{cachegroup.name}}</mat-option>
                                </mat-select>
                        </mat-form-field>
diff --git 
a/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
 
b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
index eff44869e3..1444862745 100644
--- 
a/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
+++ 
b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
@@ -25,4 +25,4 @@ limitations under the License.
        </tp-generic-table>
 </mat-card>
 
-<button class="page-fab" mat-fab title="Create a new Cache Group" 
*ngIf="auth.hasPermission('CACHE-GROUP:CREATE')" 
routerLink="new"><mat-icon>add</mat-icon></button>
+<a class="page-fab" mat-fab title="Create a new Cache Group" 
*ngIf="auth.hasPermission('CACHE-GROUP:CREATE')" 
routerLink="new"><mat-icon>add</mat-icon></a>
diff --git 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
index 3b6157e820..a10e4f98f4 100644
--- 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
+++ 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
@@ -19,7 +19,7 @@ limitations under the License.
                        <p 
*ngIf=currentUser.addressLine1>{{currentUser.addressLine1}}</p>
                        <p 
*ngIf=currentUser.addressLine2>{{currentUser.addressLine2}}</p>
                        <p *ngIf=currentUser.company>{{currentUser.company}}</p>
-                       <p *ngIf="currentUser.city || currentUser.country || 
currentUser.stateOrProvince || currentUser.postalCode"><span 
*ngIf="currentUser.city">{{currentUser.city}}<span *ngIf="currentUser.country 
|| currentUser.stateOrProvince || 
currentUser.postalCode">&nbsp;</span></span><span 
*ngIf="currentUser.stateOrProvince">{{currentUser.stateOrProvince}}<span 
*ngIf="currentUser.country || currentUser.postalCode">, </span></span><span 
*ngIf="currentUser.country">{{currentUser.country}}<span *ngIf=" [...]
+                       <p *ngIf="hasBottomAddress()"><span 
*ngIf="currentUser.city">{{currentUser.city}}<span *ngIf="currentUser.country 
|| currentUser.stateOrProvince || 
currentUser.postalCode">&nbsp;</span></span><span 
*ngIf="currentUser.stateOrProvince">{{currentUser.stateOrProvince}}<span 
*ngIf="currentUser.country || currentUser.postalCode">, </span></span><span 
*ngIf="currentUser.country">{{currentUser.country}}<span 
*ngIf="currentUser.postalCode">, </span></span><span 
*ngIf=currentUser.postalCode>{{c [...]
                </address>
                <div>
                        <h2>User Theme</h2>
@@ -75,7 +75,7 @@ limitations under the License.
                        </mat-form-field>
                </mat-card-content>
                <mat-card-actions>
-                       <button mat-raised-button style="grid-area: 
k;">Submit</button>
+                       <button mat-raised-button style="grid-area: k;" 
type="submit">Submit</button>
                        <button mat-raised-button color="accent" 
style="grid-area: l;" type="button" (click)="updatePassword()">Update 
Password</button>
                        <button type="button" mat-raised-button color="warn" 
(click)="cancelEdit()" style="grid-area: m;">Cancel</button>
                </mat-card-actions>
diff --git 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
index 851d6d3476..c29f9056b0 100644
--- 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
+++ 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
@@ -167,4 +167,48 @@ describe("CurrentuserComponent", () => {
 
                expect(updateSpy).toHaveBeenCalledTimes(2);
        }));
+
+       it("determines whether a user has a 'bottom-level' address", () => {
+               component.currentUser = null;
+               expect(component.hasBottomAddress()).toBeFalse();
+               component.currentUser = {
+                       addressLine1: null,
+                       addressLine2: null,
+                       changeLogCount: 0,
+                       city: null,
+                       company: null,
+                       country: null,
+                       email: "[email protected]",
+                       fullName: "",
+                       gid: null,
+                       id: 1,
+                       lastAuthenticated: null,
+                       lastUpdated: new Date(),
+                       localUser: false,
+                       newUser: false,
+                       phoneNumber: null,
+                       postalCode: null,
+                       publicSshKey: null,
+                       registrationSent: null,
+                       role: "",
+                       stateOrProvince: null,
+                       tenant: "",
+                       tenantId: 1,
+                       ucdn: "",
+                       uid: null,
+                       username: "",
+               };
+               expect(component.hasBottomAddress()).toBeFalse();
+               component.currentUser.city = "Townsville";
+               expect(component.hasBottomAddress()).toBeTrue();
+               component.currentUser.city = null;
+               component.currentUser.country = "Nationstan";
+               expect(component.hasBottomAddress()).toBeTrue();
+               component.currentUser.country = null;
+               component.currentUser.stateOrProvince = "Provincia";
+               expect(component.hasBottomAddress()).toBeTrue();
+               component.currentUser.stateOrProvince = null;
+               component.currentUser.postalCode = "00000";
+               expect(component.hasBottomAddress()).toBeTrue();
+       });
 });
diff --git 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
index 1e69ddfc9c..8362d33044 100644
--- 
a/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
+++ 
b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
@@ -153,4 +153,19 @@ export class CurrentuserComponent implements OnInit {
                        this.cancelEdit();
                }
        }
+
+       /**
+        * Checks if the form's user has a "bottom-level" address, meaning any
+        * combination of state/province, postal code, city, and/or country.
+        *
+        * @returns `true` if the user has a "bottom-level" address, `false`
+        * otherwise.
+        */
+       public hasBottomAddress(): boolean {
+               if (!this.currentUser) {
+                       return false;
+               }
+               const {city, country, stateOrProvince, postalCode} = 
this.currentUser;
+               return !!(city || country || stateOrProvince || postalCode);
+       }
 }
diff --git 
a/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
 
b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
index 9636f702b1..cc1ea16d22 100644
--- 
a/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
@@ -24,7 +24,7 @@ limitations under the License.
                </mat-form-field>
        </div>
        <div mat-dialog-actions>
-               <button mat-stroked-button>Submit</button>
+               <button type="submit" mat-stroked-button>Submit</button>
                <button type="button" mat-stroked-button color="warn" 
(click)="cancel()">Cancel</button>
        </div>
 </form>
diff --git 
a/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
 
b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
index 8570f35aca..832cc5522a 100644
--- 
a/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
+++ 
b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
@@ -35,7 +35,7 @@ limitations under the License.
                                </mat-form-field>
                        </div>
                        <div class="actions">
-                               <button name="timespanRefresh" 
mat-raised-button>Refresh</button>
+                               <button type="submit" name="timespanRefresh" 
mat-raised-button>Refresh</button>
                                <mat-icon *ngIf="steeringTargetsFor.size > 0" 
[matTooltip]="steeringTargetDisplay()" >assistant_direction</mat-icon>
                        </div>
                </form>
diff --git 
a/experimental/traffic-portal/src/app/core/deliveryservice/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
 
b/experimental/traffic-portal/src/app/core/deliveryservice/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
index 09e38bc85f..98b8895aed 100644
--- 
a/experimental/traffic-portal/src/app/core/deliveryservice/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/core/deliveryservice/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
@@ -36,7 +36,7 @@ limitations under the License.
                </mat-form-field>
        </div>
        <footer mat-dialog-actions>
-               <button mat-raised-button id="submit">Submit</button>
+               <button mat-raised-button type="submit">Submit</button>
                <button mat-raised-button color="warn" type="button" 
(click)="cancel()">Cancel</button>
        </footer>
 </form>
diff --git 
a/experimental/traffic-portal/src/app/core/deliveryservice/new-delivery-service/new-delivery-service.component.html
 
b/experimental/traffic-portal/src/app/core/deliveryservice/new-delivery-service/new-delivery-service.component.html
index 697b1a6d84..0bf814f6bf 100644
--- 
a/experimental/traffic-portal/src/app/core/deliveryservice/new-delivery-service/new-delivery-service.component.html
+++ 
b/experimental/traffic-portal/src/app/core/deliveryservice/new-delivery-service/new-delivery-service.component.html
@@ -16,14 +16,14 @@ limitations under the License.
                <form #form1="ngForm" ngNativeValidate ngDefaultControl 
(ngSubmit)="setOriginURL()">
                        <h2>Step 1 - Origin Server</h2>
                        <div class="form-content">
-                               <label for="origin">I want to create a Delivery 
Service for:</label>
+                               <label for="origin">I want to create a Delivery 
Service for</label>
                                <input [formControl]="originURL" type="url" 
id="origin" name="origin" placeholder="this URL" autofocus title="Must be a URL 
(should start with `http://` or `https://`)" 
pattern="(https?|HTTPS?)://[a-zA-Z][a-zA-z0-9\-]*(\.[a-zA-Z][a-zA-z0-9\-]*)*(/[\w\.]+)*/?"
 required/>
                                <label for="active-immediately">This Delivery 
Service should become active immediately</label>
                                <input [formControl]="activeImmediately" 
id="active-immediately" type="checkbox" name="active-immediately" title="This 
Delivery Service should become active immediately"/>
                        </div>
                        <div class="buttons-holder">
                                <button mat-raised-button color="warn" 
type="reset">Clear</button>
-                               <button mat-raised-button>Next</button>
+                               <button type="submit" 
mat-raised-button>Next</button>
                        </div>
                </form>
        </mat-step>
@@ -31,22 +31,22 @@ limitations under the License.
                <form #form2="ngForm" ngNativeValidate ngDefaultControl 
(ngSubmit)="setMetaInformation()">
                        <h2>Step 2 - Meta Information</h2>
                        <div class="form-content">
-                               <label for="displayName">This Delivery 
Service's name will be:</label>
+                               <label for="displayName">This Delivery 
Service's name will be</label>
                                <input type="text" autofocus title="This will 
be the name of the Delivery Service as it appears on the 'Home' screen" 
name="displayName" id="displayName" (change)="updateDisplayName()" 
[formControl]="displayName" placeholder="{{deliveryService.displayName}}" 
required>
-                               <label for="longDesc">Please briefly describe 
this Delivery Service's purpose and function here:</label>
+                               <label for="longDesc">Please briefly describe 
this Delivery Service's purpose and function</label>
                                <textarea id="longDesc" name="longDesc" 
title="No character limit - be as verbose as you like." required 
placeholder="e.g. This Delivery Service is for my website's image assets." 
[formControl]="description" rows="3"></textarea>
                        </div>
                        <div>
                                <input type="checkbox" class="sub-form" 
id="info-URL-sub-form" hidden/>
                                <label for="info-URL-sub-form" 
class="sub-form">Add Informational URL</label>
                                <div class="form-content">
-                                       <label for="infoURL" 
id="info-URL-label">For more information, people can look at this URL:</label>
+                                       <label for="infoURL" 
id="info-URL-label">For more information, people can look at this URL</label>
                                        <input [formControl]="infoURL" 
type="url" name="infoURL" id="infoURL" placeholder="e.g. 
https://company.jira.com/ticket/9001"/>
                                </div>
                        </div>
                        <div class="buttons-holder">
                                <button mat-raised-button type="reset" 
matStepperPrevious>Previous</button>
-                               <button mat-raised-button>Next</button>
+                               <button mat-raised-button 
type="submit">Next</button>
                        </div>
                </form>
        </mat-step>
@@ -54,7 +54,7 @@ limitations under the License.
                <form ngNativeValidate ngDefaultControl 
(ngSubmit)="setInfrastructureInformation()">
                        <h2>Step 3 - Infrastructure</h2>
                        <div class="form-content">
-                               <label for="cdnObject">This Delivery Service 
will be a part of this CDN:</label>
+                               <label for="cdnObject">This Delivery Service 
will be a part of this CDN</label>
                                <select [formControl]="cdnObject" 
name="cdnObject" id="cdnObject" required>
                                        <option *ngFor="let cdn of cdns" 
[selected]="cdn.id === deliveryService.cdnId" 
[ngValue]="cdn">{{cdn.name}}</option>
                                </select>
@@ -67,22 +67,22 @@ limitations under the License.
                                        <select [formControl]="dsType" 
name="routingType" id="routingType">
                                                <option *ngFor="let t of 
dsTypes" [selected]="t.id === deliveryService.typeId" 
[ngValue]="t">{{t.description}}</option>
                                        </select>
-                                       <label id="protocol">How to handle 
request protocols</label>
-                                       <mat-radio-group 
aria-labelledby="protocol" [formControl]="protocol" name="protocol">
-                                               <mat-radio-button *ngFor="let p 
of protocols" [value]="p" 
name="protocol">{{protocolToString(p)}}</mat-radio-button>
+                                       <label for="protocol">How to handle 
request protocols</label>
+                                       <mat-radio-group 
aria-labelledby="protocol" [formControl]="protocol" id="protocol" 
name="protocol">
+                                               <mat-radio-button *ngFor="let p 
of protocols" [value]="p">{{protocolToString(p)}}</mat-radio-button>
                                        </mat-radio-group>
                                        <label for="disableIPv6">Disable IPv6 
Routing</label>
                                        <input type="checkbox" id="disableIPv6" 
name="disableIPv6" [formControl]="disableIPv6"/>
                                        <fieldset name="bypass" id="bypass" 
*ngIf="bypassable(deliveryService)">
                                                <legend>Bypass Options</legend>
-                                               <label>When the Delivery 
Service traffic becomes too heavy, clients can be redirected here:</label>
+                                               <label for="bypass-loc">When 
the Delivery Service traffic becomes too heavy, clients can be redirected 
here</label>
                                                <input type="text" 
name="bypass-loc" id="bypass-loc" [formControl]="bypassLoc" title="can be 
either an IP (v4 or v6) address or a Fully Qualified Domain Name" 
pattern="(^[A-z]([A-z\d\-]*[A-z\d])?(\.[A-z]([A-z\d\-]*[A-z\d])?)*$)|(^(1\d\d|2[0-4]\d|25[0-5]|\d\d?)(\.(1\d\d|2[0-4]\d|25[0-5]|\d\d?)){3}$)|(^[a-fA-F\d]{1,4}(::?([a-fA-F\d]){1,4})*$)"
 /><!-- Yeah, I know that's pretty gnarly, and it doesn't exactly validate IPv6 
addresses but it's a good litmus test -->
                                        </fieldset>
                                </div>
                        </div>
                        <div class="buttons-holder">
                                <button mat-raised-button type="reset" 
matStepperPrevious>Previous</button>
-                               <button mat-raised-button>Submit</button>
+                               <button mat-raised-button 
type="submit">Submit</button>
                        </div>
                </form>
        </mat-step>
diff --git 
a/experimental/traffic-portal/src/app/core/servers/phys-loc/table/phys-loc-table.component.html
 
b/experimental/traffic-portal/src/app/core/servers/phys-loc/table/phys-loc-table.component.html
index 82fb885cae..c2600b0bee 100644
--- 
a/experimental/traffic-portal/src/app/core/servers/phys-loc/table/phys-loc-table.component.html
+++ 
b/experimental/traffic-portal/src/app/core/servers/phys-loc/table/phys-loc-table.component.html
@@ -26,4 +26,4 @@ limitations under the License.
        </tp-generic-table>
 </mat-card>
 
-<button class="page-fab" mat-fab title="Create a new Division" 
*ngIf="auth.hasPermission('PHYSICAL-LOCATION:CREATE')" 
routerLink="new"><mat-icon>add</mat-icon></button>
+<a class="page-fab" mat-fab title="Create a new Physical Location" 
*ngIf="auth.hasPermission('PHYSICAL-LOCATION:CREATE')" 
routerLink="new"><mat-icon>add</mat-icon></a>
diff --git 
a/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
 
b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
index 6528a7f7de..db0d273db2 100644
--- 
a/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
+++ 
b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
@@ -189,7 +189,7 @@ limitations under the License.
                                </div>
                        </fieldset>
                        <div class="buttons">
-                               <button mat-raised-button>Submit</button>
+                               <button mat-raised-button 
type="submit">Submit</button>
                        </div>
                </form>
        </mat-card-content>
diff --git 
a/experimental/traffic-portal/src/app/core/users/user-details/user-details.component.html
 
b/experimental/traffic-portal/src/app/core/users/user-details/user-details.component.html
index a0eb58ca7f..a20e3b44a5 100644
--- 
a/experimental/traffic-portal/src/app/core/users/user-details/user-details.component.html
+++ 
b/experimental/traffic-portal/src/app/core/users/user-details/user-details.component.html
@@ -106,7 +106,7 @@ limitations under the License.
                        </mat-form-field>
                </mat-card-content>
                <mat-card-actions align="end">
-                       <button mat-raised-button color="primary">Save</button>
+                       <button mat-raised-button color="primary" 
type="submit">Save</button>
                </mat-card-actions>
        </form>
 </mat-card>
diff --git 
a/experimental/traffic-portal/src/app/core/users/user-registration-dialog/user-registration-dialog.component.html
 
b/experimental/traffic-portal/src/app/core/users/user-registration-dialog/user-registration-dialog.component.html
index 5c41700a50..819c7a9325 100644
--- 
a/experimental/traffic-portal/src/app/core/users/user-registration-dialog/user-registration-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/core/users/user-registration-dialog/user-registration-dialog.component.html
@@ -32,7 +32,7 @@ limitations under the License.
                </mat-form-field>
        </mat-dialog-content>
        <mat-dialog-actions>
-               <button mat-stroked-button>Submit</button>
+               <button type="submit" mat-stroked-button>Submit</button>
                <button mat-stroked-button color="warn" type="button" 
mat-dialog-close>Cancel</button>
        </mat-dialog-actions>
 </form>
diff --git 
a/experimental/traffic-portal/src/app/core/users/users.component.html 
b/experimental/traffic-portal/src/app/core/users/users.component.html
index 5f5eb8e93f..d04532049e 100644
--- a/experimental/traffic-portal/src/app/core/users/users.component.html
+++ b/experimental/traffic-portal/src/app/core/users/users.component.html
@@ -34,7 +34,7 @@ limitations under the License.
          <a cdkMenuItem routerLink="new" color="accent" mat-mini-fab 
aria-label="Create user from scratch" title="Create user from scratch">
                <mat-icon>person_add</mat-icon>
          </a>
-         <button cdkMenuItem color="accent" mat-mini-fab color="accent" 
aria-label="Register a new user by email" title="Register a new user by email" 
(click)="register()">
+         <button type="button" cdkMenuItem color="accent" mat-mini-fab 
aria-label="Register a new user by email" title="Register a new user by email" 
(click)="register()">
                <mat-icon>contact_mail</mat-icon>
          </button>
        </menu>
diff --git a/experimental/traffic-portal/src/app/login/login.component.html 
b/experimental/traffic-portal/src/app/login/login.component.html
index 3ade57fa60..20af5767be 100644
--- a/experimental/traffic-portal/src/app/login/login.component.html
+++ b/experimental/traffic-portal/src/app/login/login.component.html
@@ -27,7 +27,7 @@ limitations under the License.
                                <tp-obscured-text-input 
[autocomplete]="passwordAutocomplete" required="true" [(value)]="p" 
name="p"></tp-obscured-text-input>
                        </mat-form-field>
                        <div>
-                               <button name="login" mat-raised-button 
color="primary">Login</button>
+                               <button name="login" mat-raised-button 
color="primary" type="submit">Login</button>
                                <button name="clear" mat-raised-button 
color="warn" type="reset">Clear</button>
                                <button name="reset" mat-button color="accent" 
type="button" (click)="resetPassword()">Forgot Password</button>
                        </div>
diff --git 
a/experimental/traffic-portal/src/app/login/reset-password-dialog/reset-password-dialog.component.html
 
b/experimental/traffic-portal/src/app/login/reset-password-dialog/reset-password-dialog.component.html
index f89270fea5..052e4c994c 100644
--- 
a/experimental/traffic-portal/src/app/login/reset-password-dialog/reset-password-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/login/reset-password-dialog/reset-password-dialog.component.html
@@ -21,6 +21,6 @@ limitations under the License.
        </mat-dialog-content>
        <mat-dialog-actions>
                <button mat-raised-button color="warn" type="button" 
mat-dialog-close>Cancel</button>
-               <button mat-raised-button color="primary">Submit</button>
+               <button mat-raised-button color="primary" 
type="submit">Submit</button>
        </mat-dialog-actions>
 </form>
diff --git 
a/experimental/traffic-portal/src/app/shared/dialogs/collection-choice-dialog/collection-choice-dialog.component.html
 
b/experimental/traffic-portal/src/app/shared/dialogs/collection-choice-dialog/collection-choice-dialog.component.html
index b60e1656f9..a790af2464 100644
--- 
a/experimental/traffic-portal/src/app/shared/dialogs/collection-choice-dialog/collection-choice-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/shared/dialogs/collection-choice-dialog/collection-choice-dialog.component.html
@@ -16,7 +16,7 @@ limitations under the License.
 <mat-dialog-content>
        <p>{{dialogData.message}}</p>
        <mat-form-field>
-               <label>{{dialogData.label}}</label>
+               <mat-label>{{dialogData.label}}</mat-label>
                <mat-select name="{{dialogData.label}}" [(ngModel)]="selected" 
required>
                        <mat-option *ngFor="let item of dialogData.collection" 
[value]="item.value">{{item.label}}</mat-option>
                </mat-select>
@@ -26,6 +26,6 @@ limitations under the License.
        </mat-form-field>
 </mat-dialog-content>
 <mat-dialog-actions>
-       <button mat-button [disabled]="!selected" 
[mat-dialog-close]="selected">Confirm</button>
-       <button mat-button [mat-dialog-close]="false">Cancel</button>
+       <button type="submit" mat-button [disabled]="!selected" 
[mat-dialog-close]="selected">Confirm</button>
+       <button type="button" mat-button 
[mat-dialog-close]="false">Cancel</button>
 </mat-dialog-actions>
diff --git 
a/experimental/traffic-portal/src/app/shared/dialogs/decision-dialog/decision-dialog.component.html
 
b/experimental/traffic-portal/src/app/shared/dialogs/decision-dialog/decision-dialog.component.html
index 8b55e5e1e8..284a62fead 100644
--- 
a/experimental/traffic-portal/src/app/shared/dialogs/decision-dialog/decision-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/shared/dialogs/decision-dialog/decision-dialog.component.html
@@ -15,6 +15,6 @@ limitations under the License.
 <h2 mat-dialog-title>{{dialogData.title}}</h2>
 <mat-dialog-content>{{dialogData.message}}</mat-dialog-content>
 <mat-dialog-actions>
-       <button mat-button [mat-dialog-close]="true">Confirm</button>
-       <button mat-button [mat-dialog-close]="false">Cancel</button>
+       <button type="submit" mat-button 
[mat-dialog-close]="true">Confirm</button>
+       <button type="button" mat-button 
[mat-dialog-close]="false">Cancel</button>
 </mat-dialog-actions>
diff --git 
a/experimental/traffic-portal/src/app/shared/dialogs/text-dialog/text-dialog.component.html
 
b/experimental/traffic-portal/src/app/shared/dialogs/text-dialog/text-dialog.component.html
index 3cbb124de0..1a5fab1947 100644
--- 
a/experimental/traffic-portal/src/app/shared/dialogs/text-dialog/text-dialog.component.html
+++ 
b/experimental/traffic-portal/src/app/shared/dialogs/text-dialog/text-dialog.component.html
@@ -15,5 +15,5 @@ limitations under the License.
 <h2 mat-dialog-title>{{dialogData.title}}</h2>
 <mat-dialog-content>{{dialogData.message}}</mat-dialog-content>
 <mat-dialog-actions>
-       <button mat-button mat-dialog-close>Ok</button>
+       <button type="submit" mat-button mat-dialog-close>Ok</button>
 </mat-dialog-actions>
diff --git 
a/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
 
b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
index 5a21c5396e..20dbf15054 100644
--- 
a/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
+++ 
b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
@@ -19,12 +19,12 @@ limitations under the License.
        <button mat-flat-button type="button" (click)="download()" title="save 
as CSV"><fa-icon [icon]="downloadIcon"></fa-icon></button>
        <button mat-flat-button type="button" 
(click)="gridAPI.sizeColumnsToFit()">Resize</button>
        <div class="toggle-columns" role="group" title="Select Table Columns">
-               <button mat-flat-button [matMenuTriggerFor]="menu">
+               <button type="button" mat-flat-button 
[matMenuTriggerFor]="menu">
                        <fa-icon [icon]="columnsIcon"></fa-icon>&nbsp;
                        <fa-icon [icon]="caretIcon" class="caret" 
[ngClass]="{'rotate': showMenu}"></fa-icon>
                </button>
                <mat-menu #menu="matMenu">
-                       <button mat-menu-item *ngFor="let c of columns" 
(click)="toggleVisibility($event, c.getColId())">
+                       <button type="button" mat-menu-item *ngFor="let c of 
columns" (click)="toggleVisibility($event, c.getColId())">
                                <mat-checkbox [checked]="c.isVisible()" 
(click)="$event.preventDefault()" [name]="c.getColDef().headerName">
                                        {{c.getColDef().headerName}}
                                </mat-checkbox>
diff --git 
a/experimental/traffic-portal/src/app/shared/navigation/tp-header/tp-header.component.html
 
b/experimental/traffic-portal/src/app/shared/navigation/tp-header/tp-header.component.html
index 87d79132cd..87f28829bc 100644
--- 
a/experimental/traffic-portal/src/app/shared/navigation/tp-header/tp-header.component.html
+++ 
b/experimental/traffic-portal/src/app/shared/navigation/tp-header/tp-header.component.html
@@ -21,37 +21,37 @@ limitations under the License.
                <ul>
                        <li *ngFor="let nav of horizNavs">
                                <a *ngIf="navShown(nav, 'anchor')" mat-button 
[routerLink]="navRouterLink(nav)">{{nav.text}}</a>
-                               <button *ngIf="navShown(nav, 'button')" 
mat-button (click)="navClick(nav)">{{nav.text}}</button>
+                               <button type="button" *ngIf="navShown(nav, 
'button')" mat-button (click)="navClick(nav)">{{nav.text}}</button>
                        </li>
                        <li>
-                               <button mat-icon-button 
[matMenuTriggerFor]="expandedMenu">
+                               <button type="button" mat-icon-button 
[matMenuTriggerFor]="expandedMenu">
                                        <mat-icon>manage_accounts</mat-icon>
                                </button>
                                <mat-menu #expandedMenu="matMenu">
-                                       <button mat-menu-item 
[matMenuTriggerFor]="themeMenu">Theme</button>
+                                       <button type="button" mat-menu-item 
[matMenuTriggerFor]="themeMenu">Theme</button>
                                        <div *ngFor="let nav of vertNavs">
                                                <a *ngIf="navShown(nav, 
'anchor')" mat-menu-item [routerLink]="navRouterLink(nav)">{{nav.text}}</a>
-                                               <button *ngIf="navShown(nav, 
'button')" mat-menu-item (click)="navClick(nav)">{{nav.text}}</button>
+                                               <button type="button" 
*ngIf="navShown(nav, 'button')" mat-menu-item 
(click)="navClick(nav)">{{nav.text}}</button>
                                        </div>
                                </mat-menu>
                        </li>
                </ul>
        </nav>
        <nav id="collapsed">
-               <button mat-icon-button 
[matMenuTriggerFor]="collapsedMenu"><mat-icon>menu</mat-icon></button>
+               <button type="button" mat-icon-button 
[matMenuTriggerFor]="collapsedMenu"><mat-icon>menu</mat-icon></button>
                <mat-menu #collapsedMenu="matMenu">
                        <li *ngFor="let nav of horizNavs">
                                <a *ngIf="navShown(nav, 'anchor')" 
mat-menu-item [routerLink]="navRouterLink(nav)">{{nav.text}}</a>
-                               <button *ngIf="navShown(nav, 'button')" 
mat-menu-item (click)="navClick(nav)">{{nav.text}}</button>
+                               <button type="button" *ngIf="navShown(nav, 
'button')" mat-menu-item (click)="navClick(nav)">{{nav.text}}</button>
                        </li>
-                       <button mat-menu-item 
[matMenuTriggerFor]="themeMenu">Theme</button>
+                       <button type="button" mat-menu-item 
[matMenuTriggerFor]="themeMenu">Theme</button>
                        <li *ngFor="let nav of vertNavs">
                                <a *ngIf="navShown(nav, 'anchor')" 
mat-menu-item [routerLink]="navRouterLink(nav)">{{nav.text}}</a>
-                               <button *ngIf="navShown(nav, 'button')" 
mat-menu-item (click)="navClick(nav)">{{nav.text}}</button>
+                               <button type="button" *ngIf="navShown(nav, 
'button')" mat-menu-item (click)="navClick(nav)">{{nav.text}}</button>
                        </li>
                </mat-menu>
        </nav>
        <mat-menu #themeMenu="matMenu">
-               <button mat-menu-item *ngFor="let theme of themeSvc.themes" 
(click)="themeSvc.loadTheme(theme)">{{theme.name}}</button>
+               <button type="button" mat-menu-item *ngFor="let theme of 
themeSvc.themes" (click)="themeSvc.loadTheme(theme)">{{theme.name}}</button>
        </mat-menu>
 </mat-toolbar>


Reply via email to