Low-level plugin event helper API
Can be used to listen to tap evens (i.e. click/touch), preventing their bubbling, or preventing normal execution. To stop either bubbling or normal execution, callback should explicitly return false. By default, bubbling and normal execution is not prevented. onTap() adds a listener to a click or touch event to element wrapped with event helper. captureTap() installs a capture phase listener and callback returning false at that moment intercepts tap before any action is taken by other listeners (i.e. PolyGerrit buttons). Sample code ``` js Gerrit.install(plugin => { plugin.hook('reply-text').onAttached(element => { if (!element.content) { return; } plugin.eventHelper(element.content).onTap(() => { console.log('reply test tapped!'); }); plugin.eventHelper(element.content).captureTap(() => { // Prevent onTap() handler from being called. return false; }); }); }); ``` Change-Id: Ie10169e2c801ce85590e4f700e6041e9c8a02bff
This commit is contained in:
@@ -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>
|
@@ -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);
|
@@ -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>
|
@@ -19,6 +19,7 @@ limitations under the License.
|
||||
<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">
|
||||
|
@@ -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');
|
||||
|
@@ -38,16 +38,16 @@ limitations under the License.
|
||||
'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 @@ limitations under the License.
|
||||
'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 @@ limitations under the License.
|
||||
'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 @@ limitations under the License.
|
||||
'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 @@ limitations under the License.
|
||||
'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 @@ limitations under the License.
|
||||
'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;
|
||||
|
Reference in New Issue
Block a user