Merge "Move diffViewMode into model"
diff --git a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
index 900b2e2..9e4416b 100644
--- a/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
+++ b/java/com/google/gerrit/entities/SubmitRequirementExpressionResult.java
@@ -39,17 +39,17 @@
   /**
    * List leaf predicates that are fulfilled, for example the expression
    *
-   * <p><i>label:code-review=+2 and branch:refs/heads/master</i>
+   * <p><i>label:Code-Review=+2 and branch:refs/heads/master</i>
    *
    * <p>has two leaf predicates:
    *
    * <ul>
-   *   <li>label:code-review=+2
+   *   <li>label:Code-Review=+2
    *   <li>branch:refs/heads/master
    * </ul>
    *
    * This method will return the leaf predicates that were fulfilled, for example if only the first
-   * predicate was fulfilled, the returned list will be equal to ["label:code-review=+2"].
+   * predicate was fulfilled, the returned list will be equal to ["label:Code-Review=+2"].
    */
   public abstract ImmutableList<String> passingAtoms();
 
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 0d7210f..74407c0 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -225,7 +225,6 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.transport.PushResult;
-import org.junit.Ignore;
 import org.junit.Test;
 
 @NoHttpd
@@ -4059,7 +4058,7 @@
       assertThat(testLabel.status).isEqualTo(SubmitRecordInfo.Label.Status.OK);
       assertThat(testLabel.appliedBy).isNull();
 
-      voteLabel(changeId, "code-review", 2);
+      voteLabel(changeId, "Code-Review", 2);
       // Code review record is satisfied after voting +2
       change = gApi.changes().id(changeId).get();
       assertThat(change.submitRecords).hasSize(2);
@@ -4166,9 +4165,9 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
+            .setName("Code-Review")
             .setSubmittabilityExpression(
-                SubmitRequirementExpression.create("label:code-review=MAX"))
+                SubmitRequirementExpression.create("label:Code-Review=MAX"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4178,13 +4177,13 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
   }
 
   @Test
@@ -4239,9 +4238,9 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
+            .setName("Code-Review")
             .setSubmittabilityExpression(
-                SubmitRequirementExpression.create("-label:code-review=MIN"))
+                SubmitRequirementExpression.create("-label:Code-Review=MIN"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4252,27 +4251,27 @@
     assertThat(change.submitRequirements).hasSize(2);
     // Requirement is satisfied because there are no votes
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     // Legacy requirement (coming from the label function definition) is not satisfied. We return
     // both legacy and non-legacy requirements in this case since their statuses are not identical.
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
 
-    voteLabel(changeId, "code-review", -1);
+    voteLabel(changeId, "Code-Review", -1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     // Requirement is still satisfied because -1 is not the max negative value
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
 
-    voteLabel(changeId, "code-review", -2);
+    voteLabel(changeId, "Code-Review", -2);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     // Requirement is now unsatisfied because -2 is the max negative value
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
   }
 
   @Test
@@ -4335,9 +4334,9 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
+            .setName("Code-Review")
             .setSubmittabilityExpression(
-                SubmitRequirementExpression.create("label:code-review=ANY"))
+                SubmitRequirementExpression.create("label:Code-Review=ANY"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4347,14 +4346,14 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 1);
+    voteLabel(changeId, "Code-Review", 1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     // Legacy and non-legacy requirements have mismatching status. Both are returned from the API.
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
   }
@@ -4368,15 +4367,15 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("verified")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:verified=+1"))
+            .setName("Verified")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Verified=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4386,18 +4385,18 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "verified", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Verified", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
 
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "verified", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Verified", Status.UNSATISFIED, /* isLegacy= */ false);
   }
 
   @Test
@@ -4409,9 +4408,9 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
+            .setName("Code-Review")
             .setApplicabilityExpression(SubmitRequirementExpression.of("project:foo"))
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4421,7 +4420,7 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.NOT_APPLICABLE, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.NOT_APPLICABLE, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
   }
@@ -4445,8 +4444,8 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
@@ -4456,36 +4455,38 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
     voteLabel(changeId, "build-cop-override", 1);
 
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.OVERRIDDEN, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.OVERRIDDEN, /* isLegacy= */ false);
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
   }
 
   @Test
-  @Ignore("Test is flaky")
+  @GerritConfig(
+      name = "experiments.enabled",
+      value = ExperimentFeaturesConstants.GERRIT_BACKEND_REQUEST_FEATURE_ENABLE_SUBMIT_REQUIREMENTS)
   public void submitRequirement_overriddenInChildProject() throws Exception {
     configSubmitRequirement(
         allProjects,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+1"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(true)
             .build());
 
-    // Override submit requirement in child project (requires code-review=+2 instead of +1)
+    // Override submit requirement in child project (requires Code-Review=+2 instead of +1)
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
@@ -4495,19 +4496,19 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 1);
+    voteLabel(changeId, "Code-Review", 1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
   }
 
   @Test
@@ -4518,8 +4519,8 @@
     configSubmitRequirement(
         allProjects,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+1"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
@@ -4529,13 +4530,13 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 1);
+    voteLabel(changeId, "Code-Review", 1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     // Legacy requirement is coming from the label MaxWithBlock function. Still unsatisfied.
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
@@ -4550,19 +4551,19 @@
     configSubmitRequirement(
         allProjects,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+1"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+1"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
-    // Override submit requirement in child project (requires code-review=+2 instead of +1).
+    // Override submit requirement in child project (requires Code-Review=+2 instead of +1).
     // Will have no effect since parent does not allow override.
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setOverrideExpression(SubmitRequirementExpression.of("label:build-cop-override=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
@@ -4572,14 +4573,14 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 1);
+    voteLabel(changeId, "Code-Review", 1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(2);
     // +1 was enough to fulfill the requirement: override in child project was ignored
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
     // Legacy requirement is coming from the label MaxWithBlock function. Still unsatisfied.
     assertSubmitRequirementStatus(
         change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ true);
@@ -4600,9 +4601,9 @@
       configSubmitRequirement(
           project,
           SubmitRequirement.builder()
-              .setName("code-review")
+              .setName("Code-Review")
               .setSubmittabilityExpression(
-                  SubmitRequirementExpression.create("label:code-review=+2"))
+                  SubmitRequirementExpression.create("label:Code-Review=+2"))
               .setAllowOverrideInChildProjects(false)
               .build());
 
@@ -4610,12 +4611,12 @@
           createChange(repo, "master", "Add a file", "foo", "content", "topic");
       String changeId = r.getChangeId();
 
-      voteLabel(changeId, "code-review", 2);
+      voteLabel(changeId, "Code-Review", 2);
 
       ChangeInfo change = gApi.changes().id(changeId).get();
       assertThat(change.submitRequirements).hasSize(1);
       assertSubmitRequirementStatus(
-          change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+          change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
 
       RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
       revision.review(ReviewInput.approve());
@@ -4629,7 +4630,7 @@
       assertThat(result.submittabilityExpressionResult().status())
           .isEqualTo(SubmitRequirementExpressionResult.Status.PASS);
       assertThat(result.submittabilityExpressionResult().expression().expressionString())
-          .isEqualTo("label:code-review=+2");
+          .isEqualTo("label:Code-Review=+2");
     }
   }
 
@@ -4645,8 +4646,8 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4656,14 +4657,14 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.UNSATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.UNSATISFIED, /* isLegacy= */ false);
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
 
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
 
     gApi.changes().id(changeId).current().submit();
 
@@ -4671,16 +4672,16 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("verified")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:verified=+1"))
+            .setName("Verified")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Verified=+1"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
-    // The new "verified" submit requirement is not returned, since this change is closed
+    // The new "Verified" submit requirement is not returned, since this change is closed
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).hasSize(1);
     assertSubmitRequirementStatus(
-        change.submitRequirements, "code-review", Status.SATISFIED, /* isLegacy= */ false);
+        change.submitRequirements, "Code-Review", Status.SATISFIED, /* isLegacy= */ false);
   }
 
   @Test
@@ -4869,15 +4870,15 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
 
     // Query the change. ChangeInfo is back-filled from the change index.
     List<ChangeInfo> changeInfos =
@@ -4889,7 +4890,7 @@
     assertThat(changeInfos).hasSize(1);
     assertSubmitRequirementStatus(
         changeInfos.get(0).submitRequirements,
-        "code-review",
+        "Code-Review",
         Status.SATISFIED,
         /* isLegacy= */ false);
   }
@@ -4906,15 +4907,15 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
     gApi.changes().id(changeId).current().submit();
 
     // Query the change. ChangeInfo is back-filled from the change index.
@@ -4927,7 +4928,7 @@
     assertThat(changeInfos).hasSize(1);
     assertSubmitRequirementStatus(
         changeInfos.get(0).submitRequirements,
-        "code-review",
+        "Code-Review",
         Status.SATISFIED,
         /* isLegacy= */ false);
   }
@@ -4937,8 +4938,8 @@
     configSubmitRequirement(
         project,
         SubmitRequirement.builder()
-            .setName("code-review")
-            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:code-review=+2"))
+            .setName("Code-Review")
+            .setSubmittabilityExpression(SubmitRequirementExpression.create("label:Code-Review=+2"))
             .setAllowOverrideInChildProjects(false)
             .build());
 
@@ -4948,11 +4949,11 @@
     ChangeInfo change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).isEmpty();
 
-    voteLabel(changeId, "code-review", -1);
+    voteLabel(changeId, "Code-Review", -1);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).isEmpty();
 
-    voteLabel(changeId, "code-review", 2);
+    voteLabel(changeId, "Code-Review", 2);
     change = gApi.changes().id(changeId).get();
     assertThat(change.submitRequirements).isEmpty();
 
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
index 6e19c39..f511683 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsEvaluatorIT.java
@@ -138,13 +138,13 @@
     SubmitRequirement sr =
         createSubmitRequirement(
             /* applicabilityExpr= */ "project:" + project.get(),
-            /* submittabilityExpr= */ "label:\"code-review=+2\"",
+            /* submittabilityExpr= */ "label:\"Code-Review=+2\"",
             /* overrideExpr= */ "");
 
     SubmitRequirementResult result = evaluator.evaluateRequirement(sr, changeData);
     assertThat(result.status()).isEqualTo(SubmitRequirementResult.Status.UNSATISFIED);
     assertThat(result.submittabilityExpressionResult().failingAtoms())
-        .containsExactly("label:\"code-review=+2\"");
+        .containsExactly("label:\"Code-Review=+2\"");
   }
 
   @Test
@@ -160,7 +160,7 @@
     SubmitRequirement sr =
         createSubmitRequirement(
             /* applicabilityExpr= */ "project:" + project.get(),
-            /* submittabilityExpr= */ "label:\"code-review=+2\"",
+            /* submittabilityExpr= */ "label:\"Code-Review=+2\"",
             /* overrideExpr= */ "label:\"build-cop-override=+1\"");
 
     SubmitRequirementResult result = evaluator.evaluateRequirement(sr, changeData);
@@ -175,7 +175,7 @@
     SubmitRequirement sr =
         createSubmitRequirement(
             /* applicabilityExpr= */ "invalid_field:invalid_value",
-            /* submittabilityExpr= */ "label:\"code-review=+2\"",
+            /* submittabilityExpr= */ "label:\"Code-Review=+2\"",
             /* overrideExpr= */ "label:\"build-cop-override=+1\"");
 
     SubmitRequirementResult result = evaluator.evaluateRequirement(sr, changeData);
@@ -206,7 +206,7 @@
     SubmitRequirement sr =
         createSubmitRequirement(
             /* applicabilityExpr= */ "project:" + project.get(),
-            /* submittabilityExpr= */ "label:\"code-review=+2\"",
+            /* submittabilityExpr= */ "label:\"Code-Review=+2\"",
             /* overrideExpr= */ "invalid_field:invalid_value");
 
     SubmitRequirementResult result = evaluator.evaluateRequirement(sr, changeData);
diff --git a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsValidationIT.java b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsValidationIT.java
index 85dfd4d..d8aa789 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsValidationIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/SubmitRequirementsValidationIT.java
@@ -50,7 +50,7 @@
                 ProjectConfig.SUBMIT_REQUIREMENT,
                 /* subsection= */ submitRequirementName,
                 /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-                /* value= */ "label:\"code-review=+2\""));
+                /* value= */ "label:\"Code-Review=+2\""));
 
     PushResult r = pushRefsMetaConfig();
     assertOkStatus(r);
@@ -77,7 +77,7 @@
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
           projectConfig.setString(
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
@@ -109,7 +109,7 @@
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ null,
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
         });
 
     PushResult r = pushRefsMetaConfig();
@@ -177,12 +177,12 @@
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
           projectConfig.setString(
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName.toLowerCase(Locale.US),
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
         });
 
     PushResult r = pushRefsMetaConfig();
@@ -204,7 +204,7 @@
                 ProjectConfig.SUBMIT_REQUIREMENT,
                 /* subsection= */ submitRequirementName,
                 /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-                /* value= */ "label:\"code-review=+2\""));
+                /* value= */ "label:\"Code-Review=+2\""));
     PushResult r = pushRefsMetaConfig();
     assertOkStatus(r);
 
@@ -214,7 +214,7 @@
                 ProjectConfig.SUBMIT_REQUIREMENT,
                 /* subsection= */ submitRequirementName.toLowerCase(Locale.US),
                 /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-                /* value= */ "label:\"code-review=+2\""));
+                /* value= */ "label:\"Code-Review=+2\""));
     r = pushRefsMetaConfig();
     assertErrorStatus(
         r,
@@ -291,7 +291,7 @@
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
           projectConfig.setString(
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
@@ -326,7 +326,7 @@
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
               /* name= */ ProjectConfig.KEY_SR_SUBMITTABILITY_EXPRESSION,
-              /* value= */ "label:\"code-review=+2\"");
+              /* value= */ "label:\"Code-Review=+2\"");
           projectConfig.setString(
               ProjectConfig.SUBMIT_REQUIREMENT,
               /* subsection= */ submitRequirementName,
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementSerializerTest.java b/javatests/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementSerializerTest.java
index a1dee1a..6993dfe 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementSerializerTest.java
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/SubmitRequirementSerializerTest.java
@@ -26,7 +26,7 @@
 public class SubmitRequirementSerializerTest {
   private static final SubmitRequirement submitReq =
       SubmitRequirement.builder()
-          .setName("code-review")
+          .setName("Code-Review")
           .setDescription(Optional.of("require code review +2"))
           .setApplicabilityExpression(SubmitRequirementExpression.of("branch(refs/heads/master)"))
           .setSubmittabilityExpression(SubmitRequirementExpression.create("label(code-review, 2+)"))
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index 61002f9..0c26f1a 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -696,7 +696,7 @@
                                 .setApplicabilityExpression(
                                     SubmitRequirementExpression.of("project:foo"))
                                 .setSubmittabilityExpression(
-                                    SubmitRequirementExpression.create("label:code-review=+2"))
+                                    SubmitRequirementExpression.create("label:Code-Review=+2"))
                                 .setAllowOverrideInChildProjects(false)
                                 .build())
                         .applicabilityExpressionResult(
@@ -708,10 +708,10 @@
                                     ImmutableList.of())))
                         .submittabilityExpressionResult(
                             SubmitRequirementExpressionResult.create(
-                                SubmitRequirementExpression.create("label:code-review=+2"),
+                                SubmitRequirementExpression.create("label:Code-Review=+2"),
                                 SubmitRequirementExpressionResult.Status.FAIL,
                                 ImmutableList.of(),
-                                ImmutableList.of("label:code-review=+2")))
+                                ImmutableList.of("label:Code-Review=+2")))
                         .build()))
             .build(),
         newProtoBuilder()
@@ -726,7 +726,7 @@
                         SubmitRequirementProto.newBuilder()
                             .setName("Code-Review")
                             .setApplicabilityExpression("project:foo")
-                            .setSubmittabilityExpression("label:code-review=+2")
+                            .setSubmittabilityExpression("label:Code-Review=+2")
                             .setAllowOverrideInChildProjects(false)
                             .build())
                     .setApplicabilityExpressionResult(
@@ -737,9 +737,9 @@
                             .build())
                     .setSubmittabilityExpressionResult(
                         SubmitRequirementExpressionResultProto.newBuilder()
-                            .setExpression("label:code-review=+2")
+                            .setExpression("label:Code-Review=+2")
                             .setStatus("FAIL")
-                            .addFailingAtoms("label:code-review=+2")
+                            .addFailingAtoms("label:Code-Review=+2")
                             .build())
                     .build())
             .build());
diff --git a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
index 48ef85d8..aed1648 100644
--- a/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
+++ b/javatests/com/google/gerrit/server/project/ProjectConfigTest.java
@@ -217,7 +217,7 @@
                 "[submit-requirement \"Code-review\"]\n"
                     + "  description =  At least one Code Review +2\n"
                     + "  applicableIf =branch(refs/heads/master)\n"
-                    + "  submittableIf =  label(code-review, +2)\n"
+                    + "  submittableIf =  label(Code-Review, +2)\n"
                     + "[submit-requirement \"api-review\"]\n"
                     + "  description =  Additional review required for API modifications\n"
                     + "  applicableIf =commit_filepath_contains(\\\"/api/.*\\\")\n"
@@ -237,7 +237,7 @@
                 .setApplicabilityExpression(
                     SubmitRequirementExpression.of("branch(refs/heads/master)"))
                 .setSubmittabilityExpression(
-                    SubmitRequirementExpression.create("label(code-review, +2)"))
+                    SubmitRequirementExpression.create("label(Code-Review, +2)"))
                 .setOverrideExpression(Optional.empty())
                 .setAllowOverrideInChildProjects(false)
                 .build(),
@@ -262,19 +262,19 @@
             .add("groups", group(developers))
             .add(
                 "project.config",
-                "[submit-requirement \"code-review\"]\n"
-                    + "  submittableIf =  label(code-review, +2)\n")
+                "[submit-requirement \"Code-Review\"]\n"
+                    + "  submittableIf =  label(Code-Review, +2)\n")
             .create();
 
     ProjectConfig cfg = read(rev);
     Map<String, SubmitRequirement> submitRequirements = cfg.getSubmitRequirementSections();
     assertThat(submitRequirements)
         .containsExactly(
-            "code-review",
+            "Code-Review",
             SubmitRequirement.builder()
-                .setName("code-review")
+                .setName("Code-Review")
                 .setSubmittabilityExpression(
-                    SubmitRequirementExpression.create("label(code-review, +2)"))
+                    SubmitRequirementExpression.create("label(Code-Review, +2)"))
                 .setAllowOverrideInChildProjects(false)
                 .build());
   }
@@ -320,8 +320,8 @@
             .add("groups", group(developers))
             .add(
                 "project.config",
-                "[submit-requirement \"code-review\"]\n"
-                    + "  applicableIf =label(code-review, +2)\n")
+                "[submit-requirement \"Code-Review\"]\n"
+                    + "  applicableIf =label(Code-Review, +2)\n")
             .create();
 
     ProjectConfig cfg = read(rev);
@@ -331,8 +331,8 @@
     assertThat(Iterables.getOnlyElement(cfg.getValidationErrors()).getMessage())
         .isEqualTo(
             "project.config: Setting a submittability expression for submit requirement"
-                + " 'code-review' is required: Missing"
-                + " submit-requirement.code-review.submittableIf");
+                + " 'Code-Review' is required: Missing"
+                + " submit-requirement.Code-Review.submittableIf");
   }
 
   @Test
@@ -953,10 +953,10 @@
         tr.commit()
             .add(
                 "project.config",
-                "[submit-requirement \"code-review\"]\n"
+                "[submit-requirement \"Code-Review\"]\n"
                     + "  description =  At least one Code Review +2\n"
                     + "  applicableIf =branch(refs/heads/master)\n"
-                    + "  submittableIf =  label(code-review, +2)\n"
+                    + "  submittableIf =  label(Code-Review, +2)\n"
                     + "[notify \"name\"]\n"
                     + "  email = example@example.com\n")
             .create();
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-column/gr-change-list-column.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-column/gr-change-list-column.ts
index 480f502..2b9abfd 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-column/gr-change-list-column.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-column/gr-change-list-column.ts
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+import '../../change/gr-submit-requirement-dashboard-hovercard/gr-submit-requirement-dashboard-hovercard';
 import {LitElement, css, html} from 'lit';
 import {customElement, property} from 'lit/decorators';
 import {ChangeInfo, SubmitRequirementStatus} from '../../../api/rest-api';
@@ -71,7 +72,9 @@
 
   renderState(icon: string, message: string) {
     return html`<span class="${icon}"
-      ><iron-icon class="${icon}" icon="gr-icons:${icon}" role="img"></iron-icon
+      ><gr-submit-requirement-dashboard-hovercard .change=${this.change}>
+      </gr-submit-requirement-dashboard-hovercard>
+      <iron-icon class="${icon}" icon="gr-icons:${icon}" role="img"></iron-icon
       >${message}</span
     >`;
   }
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 050e86d..5506bc7 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -426,7 +426,9 @@
       });
     this.cleanups.push(
       addGlobalShortcut({key: Key.ESC}, _ => this._handleEscKey()),
-      addShortcut(this, {key: Key.ENTER}, _ => this.handleOpenFile())
+      addShortcut(this, {key: Key.ENTER}, _ => this.handleOpenFile(), {
+        shouldSuppress: true,
+      })
     );
   }
 
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirement-dashboard-hovercard/gr-submit-requirement-dashboard-hovercard.ts b/polygerrit-ui/app/elements/change/gr-submit-requirement-dashboard-hovercard/gr-submit-requirement-dashboard-hovercard.ts
new file mode 100644
index 0000000..94dcbef
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirement-dashboard-hovercard/gr-submit-requirement-dashboard-hovercard.ts
@@ -0,0 +1,57 @@
+/**
+ * @license
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import '../gr-submit-requirements/gr-submit-requirements';
+import {customElement, property} from 'lit/decorators';
+import {css, html, LitElement} from 'lit';
+import {HovercardMixin} from '../../../mixins/hovercard-mixin/hovercard-mixin';
+import {ParsedChangeInfo} from '../../../types/types';
+
+// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
+const base = HovercardMixin(LitElement);
+
+@customElement('gr-submit-requirement-dashboard-hovercard')
+export class GrSubmitRequirementDashboardHovercard extends base {
+  @property({type: Object})
+  change?: ParsedChangeInfo;
+
+  static override get styles() {
+    return [
+      base.styles || [],
+      css`
+        #container {
+          padding: var(--spacing-xl);
+          padding-left: var(--spacing-s);
+        }
+      `,
+    ];
+  }
+
+  override render() {
+    return html`<div id="container" role="tooltip" tabindex="-1">
+      <gr-submit-requirements
+        .change=${this.change}
+        inHovercard
+      ></gr-submit-requirements>
+    </div>`;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-submit-requirement-dashboard-hovercard': GrSubmitRequirementDashboardHovercard;
+  }
+}
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
index e8859fd..f9d5803 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
@@ -67,6 +67,9 @@
     return [
       fontStyles,
       css`
+        :host([inHovercard]) .metadata-title {
+          display: none;
+        }
         .metadata-title {
           color: var(--deemphasized-text-color);
           padding-left: var(--metadata-horizontal-padding);
@@ -108,6 +111,7 @@
         }
         td {
           padding: var(--spacing-s);
+          white-space: nowrap;
         }
         .votes-cell {
           display: flex;
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.ts b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.ts
index 3adb0f3..173a27e 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.ts
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.ts
@@ -14,28 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import '../../../styles/gr-table-styles';
-import '../../../styles/shared-styles';
 import '../../shared/gr-list-view/gr-list-view';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-documentation-search_html';
 import {getBaseUrl} from '../../../utils/url-util';
-import {customElement, property} from '@polymer/decorators';
 import {DocResult} from '../../../types/common';
 import {fireTitleChange} from '../../../utils/event-util';
 import {appContext} from '../../../services/app-context';
 import {ListViewParams} from '../../gr-app-types';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {tableStyles} from '../../../styles/gr-table-styles';
+import {LitElement, PropertyValues, html} from 'lit';
+import {customElement, property} from 'lit/decorators';
 
 @customElement('gr-documentation-search')
-export class GrDocumentationSearch extends PolymerElement {
-  static get template() {
-    return htmlTemplate;
-  }
-
+export class GrDocumentationSearch extends LitElement {
   /**
    * URL params passed from the router.
    */
-  @property({type: Object, observer: '_paramsChanged'})
+  @property({type: Object})
   params?: ListViewParams;
 
   @property({type: Array})
@@ -54,7 +49,57 @@
     fireTitleChange(this, 'Documentation Search');
   }
 
-  _paramsChanged(params: ListViewParams) {
+  static override get styles() {
+    return [sharedStyles, tableStyles];
+  }
+
+  override render() {
+    return html` <gr-list-view
+      .filter="${this._filter}"
+      .offset="${0}"
+      .loading="${this._loading}"
+      .path="/Documentation"
+    >
+      <table id="list" class="genericList">
+        <tbody>
+          <tr class="headerRow">
+            <th class="name topHeader">Name</th>
+            <th class="name topHeader"></th>
+            <th class="name topHeader"></th>
+          </tr>
+          <tr
+            id="loading"
+            class="loadingMsg ${this.computeLoadingClass(this._loading)}"
+          >
+            <td>Loading...</td>
+          </tr>
+        </tbody>
+        <tbody class="${this.computeLoadingClass(this._loading)}">
+          ${this._documentationSearches?.map(
+            search => html`
+              <tr class="table">
+                <td class="name">
+                  <a href="${this._computeSearchUrl(search.url)}"
+                    >${search.title}</a
+                  >
+                </td>
+                <td></td>
+                <td></td>
+              </tr>
+            `
+          )}
+        </tbody>
+      </table>
+    </gr-list-view>`;
+  }
+
+  override updated(changedProperties: PropertyValues) {
+    if (changedProperties.has('params')) {
+      this._paramsChanged(this.params);
+    }
+  }
+
+  _paramsChanged(params?: ListViewParams) {
     this._loading = true;
     this._filter = params?.filter ?? '';
 
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts
deleted file mode 100644
index 95ce1ec..0000000
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_html.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @license
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {html} from '@polymer/polymer/lib/utils/html-tag';
-
-export const htmlTemplate = html`
-  <style include="shared-styles">
-    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-  </style>
-  <style include="gr-table-styles">
-    /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
-  </style>
-  <gr-list-view
-    filter="[[_filter]]"
-    offset="0"
-    loading="[[_loading]]"
-    path="/Documentation"
-  >
-    <table id="list" class="genericList">
-      <tbody>
-        <tr class="headerRow">
-          <th class="name topHeader">Name</th>
-          <th class="name topHeader"></th>
-          <th class="name topHeader"></th>
-        </tr>
-        <tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
-          <td>Loading...</td>
-        </tr>
-      </tbody>
-      <tbody class$="[[computeLoadingClass(_loading)]]">
-        <template is="dom-repeat" items="[[_documentationSearches]]">
-          <tr class="table">
-            <td class="name">
-              <a href$="[[_computeSearchUrl(item.url)]]">[[item.title]]</a>
-            </td>
-            <td></td>
-            <td></td>
-          </tr>
-        </template>
-      </tbody>
-    </table>
-  </gr-list-view>
-`;
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.ts b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.ts
index bf6a0d5..47c83da 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.ts
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search_test.ts
@@ -20,7 +20,7 @@
 import {GrDocumentationSearch} from './gr-documentation-search';
 import {page} from '../../../utils/page-wrapper-utils';
 import 'lodash/lodash';
-import {stubRestApi} from '../../../test/test-utils';
+import {queryAndAssert, stubRestApi} from '../../../test/test-utils';
 import {DocResult} from '../../../types/common';
 import {ListViewParams} from '../../gr-app-types';
 
@@ -40,10 +40,11 @@
 
   let value: ListViewParams;
 
-  setup(() => {
+  setup(async () => {
     sinon.stub(page, 'show');
     element = basicFixture.instantiate();
     counter = 0;
+    await flush();
   });
 
   suite('list with searches for documentation', () => {
@@ -87,13 +88,19 @@
     test('correct contents are displayed', async () => {
       assert.isTrue(element._loading);
       assert.equal(element.computeLoadingClass(element._loading), 'loading');
-      assert.equal(getComputedStyle(element.$.loading).display, 'block');
+      assert.equal(
+        getComputedStyle(queryAndAssert(element, '#loading')).display,
+        'block'
+      );
 
       element._loading = false;
 
       await flush();
       assert.equal(element.computeLoadingClass(element._loading), '');
-      assert.equal(getComputedStyle(element.$.loading).display, 'none');
+      assert.equal(
+        getComputedStyle(queryAndAssert(element, '#loading')).display,
+        'none'
+      );
     });
   });
 });
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
index 19f12c5..a26fa08 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-service.ts
@@ -28,6 +28,7 @@
   Key,
   Modifier,
   Binding,
+  shouldSuppress,
 } from '../../utils/dom-util';
 import {ReportingService} from '../gr-reporting/gr-reporting';
 
@@ -154,34 +155,8 @@
 
   shouldSuppress(e: KeyboardEvent) {
     if (this.shortcutsDisabled) return true;
+    if (shouldSuppress(e)) return true;
 
-    // Note that when you listen on document, then `e.currentTarget` will be the
-    // document and `e.target` will be `<gr-app>` due to shadow dom, but by
-    // using the composedPath() you can actually find the true origin of the
-    // event.
-    const rootTarget = e.composedPath()[0];
-    if (!isElementTarget(rootTarget)) return false;
-    const tagName = rootTarget.tagName;
-    const type = rootTarget.getAttribute('type');
-
-    if (
-      // Suppress shortcuts on <input> and <textarea>, but not on
-      // checkboxes, because we want to enable workflows like 'click
-      // mark-reviewed and then press ] to go to the next file'.
-      (tagName === 'INPUT' && type !== 'checkbox') ||
-      tagName === 'TEXTAREA' ||
-      // Suppress shortcuts if the key is 'enter'
-      // and target is an anchor or button or paper-tab.
-      (e.keyCode === 13 &&
-        (tagName === 'A' || tagName === 'BUTTON' || tagName === 'PAPER-TAB'))
-    ) {
-      return true;
-    }
-    const path: EventTarget[] = e.composedPath() ?? [];
-    for (const el of path) {
-      if (!isElementTarget(el)) continue;
-      if (el.tagName === 'GR-OVERLAY') return true;
-    }
     // eg: {key: "k:keydown", ..., from: "gr-diff-view"}
     let key = `${e.key}:${e.type}`;
     if (this.isInSpecificComboKeyMode(ComboKey.G)) key = 'g+' + key;
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index b6dece0..e2fa8fe 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -398,10 +398,16 @@
 export function addShortcut(
   element: HTMLElement,
   shortcut: Binding,
-  listener: (e: KeyboardEvent) => void
+  listener: (e: KeyboardEvent) => void,
+  options: {
+    shouldSuppress: boolean;
+  } = {
+    shouldSuppress: false,
+  }
 ) {
   const wrappedListener = (e: KeyboardEvent) => {
     if (e.repeat) return;
+    if (options.shouldSuppress && shouldSuppress(e)) return;
     if (eventMatchesShortcut(e, shortcut)) {
       listener(e);
     }
@@ -417,3 +423,43 @@
 export function shiftPressed(e: KeyboardEvent) {
   return e.shiftKey;
 }
+
+/**
+ * When you listen on keyboard events, then within Gerrit's web app you may want
+ * to avoid firing in certain common scenarios such as key strokes from <input>
+ * elements. But this can also be undesirable, for example Ctrl-Enter from
+ * <input> should trigger a save event.
+ *
+ * The shortcuts-service has a stateful method `shouldSuppress()` with
+ * reporting functionality, which delegates to here.
+ */
+export function shouldSuppress(e: KeyboardEvent): boolean {
+  // Note that when you listen on document, then `e.currentTarget` will be the
+  // document and `e.target` will be `<gr-app>` due to shadow dom, but by
+  // using the composedPath() you can actually find the true origin of the
+  // event.
+  const rootTarget = e.composedPath()[0];
+  if (!isElementTarget(rootTarget)) return false;
+  const tagName = rootTarget.tagName;
+  const type = rootTarget.getAttribute('type');
+
+  if (
+    // Suppress shortcuts on <input> and <textarea>, but not on
+    // checkboxes, because we want to enable workflows like 'click
+    // mark-reviewed and then press ] to go to the next file'.
+    (tagName === 'INPUT' && type !== 'checkbox') ||
+    tagName === 'TEXTAREA' ||
+    // Suppress shortcuts if the key is 'enter'
+    // and target is an anchor or button or paper-tab.
+    (e.keyCode === 13 &&
+      (tagName === 'A' || tagName === 'BUTTON' || tagName === 'PAPER-TAB'))
+  ) {
+    return true;
+  }
+  const path: EventTarget[] = e.composedPath() ?? [];
+  for (const el of path) {
+    if (!isElementTarget(el)) continue;
+    if (el.tagName === 'GR-OVERLAY') return true;
+  }
+  return false;
+}
diff --git a/polygerrit-ui/app/utils/dom-util_test.ts b/polygerrit-ui/app/utils/dom-util_test.ts
index 2993c0e..9dd5be2 100644
--- a/polygerrit-ui/app/utils/dom-util_test.ts
+++ b/polygerrit-ui/app/utils/dom-util_test.ts
@@ -22,6 +22,7 @@
   getEventPath,
   Modifier,
   querySelectorAll,
+  shouldSuppress,
   strToClassName,
 } from './dom-util';
 import {PolymerElement} from '@polymer/polymer/polymer-element';
@@ -29,6 +30,22 @@
 import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
 import {queryAndAssert} from '../test/test-utils';
 
+async function keyEventOn(
+  el: HTMLElement,
+  callback: (e: KeyboardEvent) => void,
+  keyCode = 75,
+  key = 'k'
+): Promise<KeyboardEvent> {
+  let resolve: (e: KeyboardEvent) => void;
+  const promise = new Promise<KeyboardEvent>(r => (resolve = r));
+  el.addEventListener('keydown', (e: KeyboardEvent) => {
+    callback(e);
+    resolve(e);
+  });
+  MockInteractions.keyDownOn(el, keyCode, null, key);
+  return await promise;
+}
+
 class TestEle extends PolymerElement {
   static get is() {
     return 'dom-util-test-element';
@@ -266,4 +283,53 @@
       assert.isTrue(eventMatchesShortcut(e, sShift));
     });
   });
+
+  suite('shouldSuppress', () => {
+    test('do not suppress shortcut event from <div>', async () => {
+      await keyEventOn(document.createElement('div'), e => {
+        assert.isFalse(shouldSuppress(e));
+      });
+    });
+
+    test('suppress shortcut event from <input>', async () => {
+      await keyEventOn(document.createElement('input'), e => {
+        assert.isTrue(shouldSuppress(e));
+      });
+    });
+
+    test('suppress shortcut event from <textarea>', async () => {
+      await keyEventOn(document.createElement('textarea'), e => {
+        assert.isTrue(shouldSuppress(e));
+      });
+    });
+
+    test('do not suppress shortcut event from checkbox <input>', async () => {
+      const inputEl = document.createElement('input');
+      inputEl.setAttribute('type', 'checkbox');
+      await keyEventOn(inputEl, e => {
+        assert.isFalse(shouldSuppress(e));
+      });
+    });
+
+    test('suppress shortcut event from children of <gr-overlay>', async () => {
+      const overlay = document.createElement('gr-overlay');
+      const div = document.createElement('div');
+      overlay.appendChild(div);
+      await keyEventOn(div, e => {
+        assert.isTrue(shouldSuppress(e));
+      });
+    });
+
+    test('suppress "enter" shortcut event from <a>', async () => {
+      await keyEventOn(document.createElement('a'), e => {
+        assert.isFalse(shouldSuppress(e));
+      });
+      await keyEventOn(
+        document.createElement('a'),
+        e => assert.isTrue(shouldSuppress(e)),
+        13,
+        'enter'
+      );
+    });
+  });
 });