Create gr-confirm-submit-dialog component

Move the confirm submit dialog's content into a component of its own and
provide a new plugin endpoint to allow custom content to be added to the
submit confirm dialog's message.

Bug: Issue 8794
Change-Id: Ib8cf9edb5597b620eaa13846e278e053681abf70
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index 2df5971..96b5107 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -175,7 +175,11 @@
   and link:rest-api-changes.html#revision-info[RevisionInfo] are
   passed as arguments. Similar to a form submit validation, the
   function must return true to allow the operation to continue, or
-  false to prevent it.
+  false to prevent it. The function may be called multiple times, for
+  example, if submitting a change shows a confirmation dialog, this
+  event may be called to validate that the check whether dialog can be
+  shown, and called again when the submit is confirmed to check whether
+  the actual submission action can proceed.
 
 * `comment`: Invoked when a DOM element that represents a comment is
   created. This DOM element is passed as argument. This DOM element
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index b77a66b..ad613a5 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -124,3 +124,20 @@
 
 === header-title
 This endpoint wraps the title-text in the application header.
+
+=== confirm-submit-change
+This endpoint is inside the confirm submit dialog. By default it displays a
+generic confirmation message regarding submission of the change. Plugins may add
+content to this message or replace it entirely.
+
+In addition to default parameters, the following are available:
+
+* `change`
++
+The change beinng potentially submitted, an instance of
+link:rest-api-changes.html#change-info[ChangeInfo]
+
+* `action`
++
+The submit action, including the title and label, an instance of
+link:rest-api-changes.html#action-info[ActionInfo]
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index f8c1ff4..4f466f4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -24,6 +24,7 @@
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
 <link rel="import" href="../../shared/gr-icons/gr-icons.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
@@ -34,6 +35,7 @@
 <link rel="import" href="../gr-confirm-move-dialog/gr-confirm-move-dialog.html">
 <link rel="import" href="../gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html">
 <link rel="import" href="../gr-confirm-revert-dialog/gr-confirm-revert-dialog.html">
+<link rel="import" href="../gr-confirm-submit-dialog/gr-confirm-submit-dialog.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
 <dom-module id="gr-change-actions">
@@ -193,6 +195,13 @@
           on-confirm="_handleAbandonDialogConfirm"
           on-cancel="_handleConfirmDialogCancel"
           hidden></gr-confirm-abandon-dialog>
+      <gr-confirm-submit-dialog
+          id="confirmSubmitDialog"
+          class="confirmDialog"
+          change="[[change]]"
+          action="[[revisionActions.submit]]"
+          on-cancel="_handleConfirmDialogCancel"
+          on-confirm="_handleSubmitConfirm" hidden></gr-confirm-submit-dialog>
       <gr-confirm-dialog id="createFollowUpDialog"
           class="confirmDialog"
           confirm-label="Create"
@@ -238,25 +247,6 @@
           Do you really want to delete the edit?
         </div>
       </gr-confirm-dialog>
-      <gr-confirm-dialog
-          id="confirmSubmitDialog"
-          class="confirmDialog"
-          confirm-label="Submit"
-          confirm-on-enter
-          on-cancel="_handleConfirmDialogCancel"
-          on-confirm="_handleSubmitConfirm">
-        <div class="header" slot="header">
-          Submit Change
-        </div>
-        <div class="main" slot="main">
-          <p>
-            Are you sure you want to to <strong>submit</strong> this change?
-          </p>
-          <p class="changeSubject">
-            <strong>[[change.subject]]</strong>
-          </p>
-        </div>
-      </gr-confirm-dialog>
     </gr-overlay>
     <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 67d28f4..fa65075 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -252,8 +252,8 @@
       assert.deepEqual(result, actions);
     });
 
-    test('submit change', done => {
-      element.$.confirmSubmitDialog.$.confirm.focus = done;
+    test('submit change', () => {
+      const showSpy = sandbox.spy(element, '_showActionDialog');
       sandbox.stub(element.$.restAPI, 'getFromProjectLookup')
           .returns(Promise.resolve('test'));
       sandbox.stub(element, 'fetchChangeUpdates',
@@ -270,6 +270,9 @@
       const submitButton = element.$$('gr-button[data-action-key="submit"]');
       assert.ok(submitButton);
       MockInteractions.tap(submitButton);
+
+      flushAsynchronousOperations();
+      assert.isTrue(showSpy.calledWith(element.$.confirmSubmitDialog));
     });
 
     test('_handleSubmitConfirm', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
new file mode 100644
index 0000000..ad14a18
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.html
@@ -0,0 +1,56 @@
+<!--
+@license
+Copyright (C) 2018 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+
+<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+
+<dom-module id="gr-confirm-submit-dialog">
+  <template>
+    <style include="shared-styles">
+      #dialog {
+        min-width: 40em;
+      }
+      p {
+        margin-bottom: 1em;
+      }
+    </style>
+    <gr-confirm-dialog
+        id="dialog"
+        confirm-label="Continue"
+        confirm-on-enter
+        on-cancel="_handleCancelTap"
+        on-confirm="_handleConfirmTap">
+      <div class="header" slot="header">
+        [[action.label]]
+      </div>
+      <div class="main" slot="main">
+        <gr-endpoint-decorator name="confirm-submit-change">
+          <p>
+            Ready to submit &ldquo;<strong>[[change.subject]]</strong>&rdquo;?
+          </p>
+          <gr-endpoint-param name="change" value="[[change]]"></gr-endpoint-param>
+          <gr-endpoint-param name="action" value="[[action]]"></gr-endpoint-param>
+        </gr-endpoint-decorator>
+      </div>
+    </gr-confirm-dialog>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+  <script src="gr-confirm-submit-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
new file mode 100644
index 0000000..bda79a1
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
@@ -0,0 +1,65 @@
+/**
+ * @license
+ * Copyright (C) 2018 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.
+ */
+(function() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-confirm-submit-dialog',
+
+    /**
+     * Fired when the confirm button is pressed.
+     *
+     * @event confirm
+     */
+
+    /**
+     * Fired when the cancel button is pressed.
+     *
+     * @event cancel
+     */
+
+    properties: {
+      /**
+       * @type {{
+       *    subject: string,
+       *  }}
+       */
+      change: Object,
+
+      /**
+       * @type {{
+       *    label: string,
+       *  }}
+       */
+      action: Object,
+    },
+
+    resetFocus(e) {
+      this.$.dialog.resetFocus();
+    },
+
+    _handleConfirmTap(e) {
+      e.preventDefault();
+      this.dispatchEvent(new CustomEvent('confirm', {bubbles: false}));
+    },
+
+    _handleCancelTap(e) {
+      e.preventDefault();
+      this.dispatchEvent(new CustomEvent('cancel', {bubbles: false}));
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
new file mode 100644
index 0000000..86c15f6
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2018 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-confirm-submit-dialog</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<script src="../../../bower_components/page/page.js"></script>
+
+<link rel="import" href="gr-confirm-submit-dialog.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-confirm-submit-dialog></gr-confirm-submit-dialog>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-file-list-header tests', () => {
+    let element;
+    let sandbox;
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+      element = fixture('basic');
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('display', () => {
+      element.action = {label: 'my-label'};
+      element.change = {subject: 'my-subject'};
+      flushAsynchronousOperations();
+      const header = element.$$('.header');
+      assert.equal(header.textContent.trim(), 'my-label');
+
+      const message = element.$$('.main p');
+      assert.notEqual(message.textContent.length, 0);
+      assert.notEqual(message.textContent.indexOf('my-subject'), -1);
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
index 8300dd6..7017212 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
@@ -73,5 +73,11 @@
 
       assert.isTrue(handleConfirmStub.called);
     });
+
+    test('resetFocus', () => {
+      const focusStub = sandbox.stub(element.$.confirm, 'focus');
+      element.resetFocus();
+      assert.isTrue(focusStub.calledOnce);
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 5a5dbcd..6e61c8e 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -71,6 +71,7 @@
     'change/gr-confirm-move-dialog/gr-confirm-move-dialog_test.html',
     'change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html',
     'change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html',
+    'change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_test.html',
     'change/gr-download-dialog/gr-download-dialog_test.html',
     'change/gr-file-list-header/gr-file-list-header_test.html',
     'change/gr-file-list/gr-file-list_test.html',