Merge "Low-level plugin event helper API"
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
new file mode 100644
index 0000000..d62ac99
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.html
@@ -0,0 +1,21 @@
+<!--
+Copyright (C) 2017 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">
+
+<dom-module id="gr-event-helper">
+ <script src="gr-event-helper.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
new file mode 100644
index 0000000..e750c07
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper.js
@@ -0,0 +1,69 @@
+// Copyright (C) 2017 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(window) {
+ 'use strict';
+
+ function GrEventHelper(element) {
+ this.element = element;
+ this._unsubscribers = [];
+ }
+
+ /**
+ * Add a callback to element click or touch.
+ * The callback may return false to prevent event bubbling.
+ * @param {function(Event):boolean} callback
+ * @return {function()} Unsubscribe function.
+ */
+ GrEventHelper.prototype.onTap = function(callback) {
+ return this._listen(this.element, callback);
+ };
+
+ /**
+ * Add a callback to element click or touch ahead of normal flow.
+ * Callback is installed on parent during capture phase.
+ * https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
+ * The callback may return false to cancel regular event listeners.
+ * @param {function(Event):boolean} callback
+ * @return {function()} Unsubscribe function.
+ */
+ GrEventHelper.prototype.captureTap = function(callback) {
+ return this._listen(this.element.parentElement, callback, {capture: true});
+ };
+
+ GrEventHelper.prototype._listen = function(container, callback, opt_options) {
+ const capture = opt_options && opt_options.capture;
+ const handler = e => {
+ if (e.path.indexOf(this.element) !== -1) {
+ let mayContinue = true;
+ try {
+ mayContinue = callback(e);
+ } catch (e) {
+ console.warn(`Plugin error handing event: ${e}`);
+ }
+ if (mayContinue === false) {
+ e.stopImmediatePropagation();
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+ };
+ container.addEventListener('tap', handler, capture);
+ const unsubscribe = () =>
+ container.removeEventListener('tap', handler, capture);
+ this._unsubscribers.push(unsubscribe);
+ return unsubscribe;
+ };
+
+ window.GrEventHelper = GrEventHelper;
+})(window);
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
new file mode 100644
index 0000000..9d42851
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 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-event-helper</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"/>
+<link rel="import" href="gr-event-helper.html"/>
+
+<script>void(0);</script>
+
+<dom-element id="some-element">
+ <script>
+ Polymer({
+ is: 'some-element',
+ properties: {
+ fooBar: {
+ type: Object,
+ notify: true,
+ },
+ },
+ });
+ </script>
+</dom-element>
+
+<test-fixture id="basic">
+ <template>
+ <some-element></some-element>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-event-helper tests', () => {
+ let element;
+ let instance;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ instance = new GrEventHelper(element);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ test('onTap()', done => {
+ instance.onTap(() => {
+ done();
+ });
+ element.fire('tap');
+ });
+
+ test('onTap() cancel', () => {
+ const tapStub = sandbox.stub();
+ element.parentElement.addEventListener('tap', tapStub);
+ instance.onTap(() => false);
+ element.fire('tap');
+ flushAsynchronousOperations();
+ assert.isFalse(tapStub.called);
+ });
+
+ test('captureTap()', done => {
+ instance.captureTap(() => {
+ done();
+ });
+ element.fire('tap');
+ });
+
+ test('captureTap() cancels tap()', () => {
+ const tapStub = sandbox.stub();
+ element.addEventListener('tap', tapStub);
+ instance.captureTap(() => false);
+ element.fire('tap');
+ flushAsynchronousOperations();
+ assert.isFalse(tapStub.called);
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index 53f889f..4133600 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -19,6 +19,7 @@
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../plugins/gr-attribute-helper/gr-attribute-helper.html">
<link rel="import" href="../../plugins/gr-dom-hooks/gr-dom-hooks.html">
+<link rel="import" href="../../plugins/gr-event-helper/gr-event-helper.html">
<link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html">
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 0fe2da3..8f78dd3 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -198,6 +198,10 @@
return new GrAttributeHelper(element);
};
+ Plugin.prototype.eventHelper = function(element) {
+ return new GrEventHelper(element);
+ };
+
Plugin.prototype.popup = function(moduleName) {
if (typeof moduleName !== 'string') {
throw new Error('deprecated, use deprecated.popup');
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 38755c4..2dba4fc 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -38,16 +38,16 @@
'admin/gr-create-group-dialog/gr-create-group-dialog_test.html',
'admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html',
'admin/gr-create-project-dialog/gr-create-project-dialog_test.html',
- 'admin/gr-group/gr-group_test.html',
'admin/gr-group-audit-log/gr-group-audit-log_test.html',
'admin/gr-group-members/gr-group-members_test.html',
+ 'admin/gr-group/gr-group_test.html',
'admin/gr-permission/gr-permission_test.html',
'admin/gr-plugin-list/gr-plugin-list_test.html',
- 'admin/gr-project/gr-project_test.html',
'admin/gr-project-access/gr-project-access_test.html',
'admin/gr-project-commands/gr-project-commands_test.html',
'admin/gr-project-detail-list/gr-project-detail-list_test.html',
'admin/gr-project-list/gr-project-list_test.html',
+ 'admin/gr-project/gr-project_test.html',
'admin/gr-rule-editor/gr-rule-editor_test.html',
'change-list/gr-change-list-item/gr-change-list-item_test.html',
'change-list/gr-change-list-view/gr-change-list-view_test.html',
@@ -55,8 +55,8 @@
'change/gr-account-entry/gr-account-entry_test.html',
'change/gr-account-list/gr-account-list_test.html',
'change/gr-change-actions/gr-change-actions_test.html',
- 'change/gr-change-metadata/gr-change-metadata_test.html',
'change/gr-change-metadata/gr-change-metadata-it_test.html',
+ 'change/gr-change-metadata/gr-change-metadata_test.html',
'change/gr-change-view/gr-change-view_test.html',
'change/gr-comment-list/gr-comment-list_test.html',
'change/gr-commit-info/gr-commit-info_test.html',
@@ -65,22 +65,22 @@
'change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html',
'change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html',
'change/gr-download-dialog/gr-download-dialog_test.html',
- 'change/gr-label-scores/gr-label-scores_test.html',
- 'change/gr-label-score-row/gr-label-score-row_test.html',
- 'change/gr-file-list/gr-file-list_test.html',
'change/gr-file-list-header/gr-file-list-header_test.html',
+ 'change/gr-file-list/gr-file-list_test.html',
+ 'change/gr-label-score-row/gr-label-score-row_test.html',
+ 'change/gr-label-scores/gr-label-scores_test.html',
'change/gr-message/gr-message_test.html',
'change/gr-messages-list/gr-messages-list_test.html',
'change/gr-related-changes-list/gr-related-changes-list_test.html',
- 'change/gr-reply-dialog/gr-reply-dialog_test.html',
'change/gr-reply-dialog/gr-reply-dialog-it_test.html',
+ 'change/gr-reply-dialog/gr-reply-dialog_test.html',
'change/gr-reviewer-list/gr-reviewer-list_test.html',
'core/gr-account-dropdown/gr-account-dropdown_test.html',
'core/gr-error-manager/gr-error-manager_test.html',
'core/gr-main-header/gr-main-header_test.html',
'core/gr-navigation/gr-navigation_test.html',
- 'core/gr-router/gr-router_test.html',
'core/gr-reporting/gr-reporting_test.html',
+ 'core/gr-router/gr-router_test.html',
'core/gr-search-bar/gr-search-bar_test.html',
'diff/gr-comment-api/gr-comment-api_test.html',
'diff/gr-diff-builder/gr-diff-builder_test.html',
@@ -102,6 +102,7 @@
'diff/gr-syntax-layer/gr-syntax-layer_test.html',
'diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html',
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
+ 'plugins/gr-event-helper/gr-event-helper_test.html',
'plugins/gr-external-style/gr-external-style_test.html',
'plugins/gr-plugin-host/gr-plugin-host_test.html',
'plugins/gr-popup-interface/gr-plugin-popup_test.html',
@@ -119,9 +120,8 @@
'shared/gr-account-label/gr-account-label_test.html',
'shared/gr-account-link/gr-account-link_test.html',
'shared/gr-alert/gr-alert_test.html',
- 'shared/gr-autocomplete/gr-autocomplete_test.html',
- 'shared/gr-textarea/gr-textarea_test.html',
'shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html',
+ 'shared/gr-autocomplete/gr-autocomplete_test.html',
'shared/gr-avatar/gr-avatar_test.html',
'shared/gr-button/gr-button_test.html',
'shared/gr-change-star/gr-change-star_test.html',
@@ -133,20 +133,21 @@
'shared/gr-editable-content/gr-editable-content_test.html',
'shared/gr-editable-label/gr-editable-label_test.html',
'shared/gr-formatted-text/gr-formatted-text_test.html',
- 'shared/gr-page-nav/gr-page-nav_test.html',
'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
'shared/gr-linked-chip/gr-linked-chip_test.html',
'shared/gr-linked-text/gr-linked-text_test.html',
'shared/gr-list-view/gr-list-view_test.html',
+ 'shared/gr-page-nav/gr-page-nav_test.html',
'shared/gr-rest-api-interface/gr-auth_test.html',
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
'shared/gr-select/gr-select_test.html',
'shared/gr-storage/gr-storage_test.html',
- 'shared/gr-tooltip/gr-tooltip_test.html',
+ 'shared/gr-textarea/gr-textarea_test.html',
'shared/gr-tooltip-content/gr-tooltip-content_test.html',
+ 'shared/gr-tooltip/gr-tooltip_test.html',
];
for (let file of elements) {
file = elementsPath + file;