Fix some inconsistencies in the scouting web page

When migrating to rules_js and Cypress, I found a few inconsistencies
that we should address:
1. Some pages used a `page-title` ID element for the header instead of
   using the standard `.header` class.
2. Some of the IDs we generated are technically invalid. Cypress
   doesn't handle IDs with spaces.

This patch addresses both the inconsistencies. We use the same header
setup everywhere now. And we generate valid IDs.

The ID fixing function uses `replaceAll` which is only available
starting in ECMAScript 2021. So I upgraded to that version. It doesn't
appear to break anything.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: Id4ff050aab57f86e187e06de395dda4f3e3424bf
diff --git a/scouting/scouting_test.ts b/scouting/scouting_test.ts
index 8623fa1..cbeffc1 100644
--- a/scouting/scouting_test.ts
+++ b/scouting/scouting_test.ts
@@ -327,7 +327,7 @@
     // Navigate to Notes Page.
     await loadPage();
     await element(by.cssContainingText('.nav-link', 'Notes')).click();
-    expect(await element(by.id('page-title')).getText()).toEqual('Notes');
+    expect(await getHeadingText()).toEqual('Notes');
 
     // Add first team.
     await setTextboxByIdTo('team_number_notes', '1234');
@@ -336,7 +336,7 @@
     // Add note and select keyword for first team.
     expect(await element(by.id('team-key-1')).getText()).toEqual('1234');
     await element(by.id('text-input-1')).sendKeys('Good Driving');
-    await element(by.id('Good Driving_0')).click();
+    await element(by.id('good_driving_0')).click();
 
     // Navigate to add team selection and add another team.
     await element(by.id('add-team-button')).click();
@@ -346,7 +346,7 @@
     // Add note and select keyword for second team.
     expect(await element(by.id('team-key-2')).getText()).toEqual('1235');
     await element(by.id('text-input-2')).sendKeys('Bad Driving');
-    await element(by.id('Bad Driving_1')).click();
+    await element(by.id('bad_driving_1')).click();
 
     // Submit Notes.
     await element(by.buttonText('Submit')).click();
@@ -359,7 +359,7 @@
     // Navigate to Notes Page.
     await loadPage();
     await element(by.cssContainingText('.nav-link', 'Notes')).click();
-    expect(await element(by.id('page-title')).getText()).toEqual('Notes');
+    expect(await getHeadingText()).toEqual('Notes');
 
     // Add first team.
     await setTextboxByIdTo('team_number_notes', '1234');
@@ -395,9 +395,7 @@
     // Navigate to Driver Ranking Page.
     await loadPage();
     await element(by.cssContainingText('.nav-link', 'Driver Ranking')).click();
-    expect(await element(by.id('page-title')).getText()).toEqual(
-      'Driver Ranking'
-    );
+    expect(await getHeadingText()).toEqual('Driver Ranking');
 
     // Input match and team numbers.
     await setTextboxByIdTo('match_number_selection', '11');
diff --git a/scouting/www/driver_ranking/driver_ranking.ng.html b/scouting/www/driver_ranking/driver_ranking.ng.html
index 452359c..1fa1bc8 100644
--- a/scouting/www/driver_ranking/driver_ranking.ng.html
+++ b/scouting/www/driver_ranking/driver_ranking.ng.html
@@ -1,4 +1,6 @@
-<h2 id="page-title">Driver Ranking</h2>
+<div class="header">
+  <h2>Driver Ranking</h2>
+</div>
 
 <ng-container [ngSwitch]="section">
   <div *ngSwitchCase="'TeamSelection'">
diff --git a/scouting/www/notes/notes.component.ts b/scouting/www/notes/notes.component.ts
index f503e2d..c746ef9 100644
--- a/scouting/www/notes/notes.component.ts
+++ b/scouting/www/notes/notes.component.ts
@@ -175,4 +175,8 @@
     this.errorMessage = '';
     this.section = 'TeamSelection';
   }
+
+  labelToId(label: String): String {
+    return label.replaceAll(' ', '_').toLowerCase();
+  }
 }
diff --git a/scouting/www/notes/notes.ng.html b/scouting/www/notes/notes.ng.html
index b51a7ce..cb5e8ef 100644
--- a/scouting/www/notes/notes.ng.html
+++ b/scouting/www/notes/notes.ng.html
@@ -1,4 +1,6 @@
-<h2 id="page-title">Notes</h2>
+<div class="header">
+  <h2>Notes</h2>
+</div>
 
 <ng-container [ngSwitch]="section">
   <div *ngSwitchCase="'TeamSelection'">
@@ -58,7 +60,7 @@
                 class="form-check-input"
                 [(ngModel)]="newData[i]['keywordsData'][key]"
                 type="checkbox"
-                id="{{KEYWORD_CHECKBOX_LABELS[key]}}_{{i}}"
+                id="{{labelToId(KEYWORD_CHECKBOX_LABELS[key])}}_{{i}}"
                 name="{{KEYWORD_CHECKBOX_LABELS[key]}}"
               />
               <label
@@ -81,7 +83,7 @@
                 class="form-check-input"
                 [(ngModel)]="newData[i]['keywordsData'][key]"
                 type="checkbox"
-                id="{{KEYWORD_CHECKBOX_LABELS[key]}}"
+                id="{{labelToId(KEYWORD_CHECKBOX_LABELS[key])}}"
                 name="{{KEYWORD_CHECKBOX_LABELS[key]}}"
               />
               <label
diff --git a/tsconfig.json b/tsconfig.json
index aed3439..16fe795 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,8 +3,8 @@
     "experimentalDecorators": true,
     "strict": false,
     "noImplicitAny": false,
-    "target": "es6",
-    "lib": ["es6", "dom", "dom.iterable"],
+    "target": "es2021",
+    "lib": ["es2021", "dom", "dom.iterable"],
     "moduleResolution": "node"
   },
   "bazelOptions": {