Merge changes from topic "ts-syntax-layer"
* changes:
Convert files to typescript
Rename files to preserve history
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index 811b011..b311efc 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -165,6 +165,7 @@
margin-bottom: var(--spacing-l);
max-height: var(--relation-chain-max-height, 2em);
overflow: hidden;
+ position: relative; /* for arrowToCurrentChange to have position:absolute and be hidden */
}
.commitContainer {
display: flex;
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index 9b6d674..b28b33a 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -222,17 +222,6 @@
return GerritNav.getUrlForChangeById(changeNum, project, opt_patchNum);
}
- _computeChangeContainerClass(currentChange, relatedChange) {
- const classes = ['changeContainer'];
- if ([relatedChange, currentChange].includes(undefined)) {
- return classes;
- }
- if (this._changesEqual(relatedChange, currentChange)) {
- classes.push('thisChange');
- }
- return classes.join(' ');
- }
-
/**
* Do the given objects describe the same change? Compares the changes by
* their numbers.
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
index 1bc99e6..d4cd0f6 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
@@ -37,9 +37,8 @@
.changeContainer {
display: flex;
}
- .changeContainer.thisChange:before {
- content: '➔';
- width: 1.2em;
+ .arrowToCurrentChange {
+ position: absolute;
}
h4,
section div {
@@ -105,9 +104,15 @@
items="[[_relatedResponse.changes]]"
as="related"
>
- <div
- class$="rightIndent [[_computeChangeContainerClass(change, related)]]"
- >
+ <template is="dom-if" if="[[_changesEqual(related, change)]]">
+ <span
+ role="img"
+ class="arrowToCurrentChange"
+ aria-label="Arrow marking current change"
+ >➔</span
+ >
+ </template>
+ <div class="rightIndent changeContainer">
<a
href$="[[_computeChangeURL(related._change_number, related.project, related._revision_number)]]"
class$="[[_computeLinkClass(related)]]"
@@ -131,7 +136,15 @@
items="[[_submittedTogether.changes]]"
as="related"
>
- <div class$="[[_computeChangeContainerClass(change, related)]]">
+ <template is="dom-if" if="[[_changesEqual(related, change)]]">
+ <span
+ role="img"
+ class="arrowToCurrentChange"
+ aria-label="Arrow marking current change"
+ >➔</span
+ >
+ </template>
+ <div class="changeContainer">
<a
href$="[[_computeChangeURL(related._number, related.project)]]"
class$="[[_computeLinkClass(related)]]"
@@ -143,6 +156,8 @@
tabindex="-1"
title="Submittable"
class$="submittableCheck [[_computeLinkClass(related)]]"
+ role="img"
+ aria-label="Submittable"
>✓</span
>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
index 7cce9e2..e4aeccf 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.js
@@ -209,19 +209,6 @@
]);
});
- test('_computeChangeContainerClass', () => {
- const change1 = {change_id: 123, _number: 0};
- const change2 = {change_id: 456, _change_number: 1};
- const change3 = {change_id: 123, _number: 2};
-
- assert.notEqual(element._computeChangeContainerClass(
- change1, change1).indexOf('thisChange'), -1);
- assert.equal(element._computeChangeContainerClass(
- change1, change2).indexOf('thisChange'), -1);
- assert.equal(element._computeChangeContainerClass(
- change1, change3).indexOf('thisChange'), -1);
- });
-
test('_changesEqual', () => {
const change1 = {change_id: 123, _number: 0};
const change2 = {change_id: 456, _number: 1};
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
index 8eeb184..6fc7a17 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper.ts
@@ -65,9 +65,8 @@
/**
* Get value of the property from wrapped object. Waits for the property
* to be initialized if it isn't defined.
- *
*/
- get(name: string) {
+ get(name: string): Promise<unknown> {
if (this._elementHasProperty(name)) {
return Promise.resolve(this.element[name]);
}
@@ -80,7 +79,7 @@
});
this._promises.set(name, promise);
}
- return this._promises.get(name);
+ return this._promises.get(name)!;
}
/**
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
deleted file mode 100644
index 72cdc46..0000000
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * @license
- * 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.
- */
-import '../../shared/gr-js-api-interface/gr-js-api-interface.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-endpoint-decorator_html.js';
-import {getPluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints.js';
-import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
-
-const INIT_PROPERTIES_TIMEOUT_MS = 10000;
-
-/** @extends PolymerElement */
-class GrEndpointDecorator extends GestureEventListeners(
- LegacyElementMixin(
- PolymerElement)) {
- static get template() { return htmlTemplate; }
-
- static get is() { return 'gr-endpoint-decorator'; }
-
- static get properties() {
- return {
- name: String,
- /** @type {!Map} */
- _domHooks: {
- type: Map,
- value() { return new Map(); },
- },
- /**
- * This map prevents importing the same endpoint twice.
- * Without caching, if a plugin is loaded after the loaded plugins
- * callback fires, it will be imported twice and appear twice on the page.
- *
- * @type {!Map}
- */
- _initializedPlugins: {
- type: Map,
- value() { return new Map(); },
- },
- };
- }
-
- /** @override */
- detached() {
- super.detached();
- for (const [el, domHook] of this._domHooks) {
- domHook.handleInstanceDetached(el);
- }
- getPluginEndpoints().onDetachedEndpoint(this.name, this._endpointCallBack);
- }
-
- _initDecoration(name, plugin, slot) {
- const el = document.createElement(name);
- return this._initProperties(el, plugin,
- this.getContentChildren().find(
- el => el.nodeName !== 'GR-ENDPOINT-PARAM'))
- .then(el => {
- const slotEl = slot ?
- this.querySelector(`gr-endpoint-slot[name=${slot}]`) :
- null;
- if (slot && slotEl) {
- slotEl.parentNode.insertBefore(el, slotEl.nextSibling);
- } else {
- this._appendChild(el);
- }
- return el;
- });
- }
-
- _initReplacement(name, plugin) {
- this.getContentChildNodes()
- .filter(node => node.nodeName !== 'GR-ENDPOINT-PARAM')
- .forEach(node => node.remove());
- const el = document.createElement(name);
- return this._initProperties(el, plugin).then(
- el => this._appendChild(el));
- }
-
- _getEndpointParams() {
- return Array.from(
- this.querySelectorAll('gr-endpoint-param'));
- }
-
- /**
- * @param {!Element} el
- * @param {!Object} plugin
- * @param {!Element=} opt_content
- * @return {!Promise<Element>}
- */
- _initProperties(el, plugin, opt_content) {
- el.plugin = plugin;
- if (opt_content) {
- el.content = opt_content;
- }
- const expectProperties = this._getEndpointParams().map(paramEl => {
- const helper = plugin.attributeHelper(paramEl);
- const paramName = paramEl.getAttribute('name');
- return helper.get('value').then(
- value => helper.bind('value',
- value => plugin.attributeHelper(el).set(paramName, value))
- );
- });
- let timeoutId;
- const timeout = new Promise(
- resolve => timeoutId = setTimeout(() => {
- console.warn(
- 'Timeout waiting for endpoint properties initialization: ' +
- `plugin ${plugin.getPluginName()}, endpoint ${this.name}`);
- }, INIT_PROPERTIES_TIMEOUT_MS));
- return Promise.race([timeout, Promise.all(expectProperties)])
- .then(() => el)
- .finally(() => {
- if (timeoutId) clearTimeout(timeoutId);
- });
- }
-
- _appendChild(el) {
- return this.root.appendChild(el);
- }
-
- _initModule({moduleName, plugin, type, domHook, slot}) {
- const name = plugin.getPluginName() + '.' + moduleName;
- if (this._initializedPlugins.get(name)) {
- return;
- }
- let initPromise;
- switch (type) {
- case 'decorate':
- initPromise = this._initDecoration(moduleName, plugin, slot);
- break;
- case 'replace':
- initPromise = this._initReplacement(moduleName, plugin);
- break;
- }
- if (!initPromise) {
- console.warn('Unable to initialize module ' + name);
- }
- this._initializedPlugins.set(name, true);
- initPromise.then(el => {
- domHook.handleInstanceAttached(el);
- this._domHooks.set(el, domHook);
- });
- }
-
- /** @override */
- ready() {
- super.ready();
- this._endpointCallBack = this._initModule.bind(this);
- getPluginEndpoints().onNewEndpoint(this.name, this._endpointCallBack);
- if (this.name) {
- getPluginLoader().awaitPluginsLoaded()
- .then(() => getPluginEndpoints().getAndImportPlugins(this.name))
- .then(() =>
- getPluginEndpoints()
- .getDetails(this.name)
- .forEach(this._initModule, this)
- );
- }
- }
-}
-
-customElements.define(GrEndpointDecorator.is, GrEndpointDecorator);
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts
new file mode 100644
index 0000000..674297d
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.ts
@@ -0,0 +1,199 @@
+/**
+ * @license
+ * 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.
+ */
+import '../../shared/gr-js-api-interface/gr-js-api-interface';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-endpoint-decorator_html';
+import {
+ getPluginEndpoints,
+ ModuleInfo,
+} from '../../shared/gr-js-api-interface/gr-plugin-endpoints';
+import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
+import {customElement, property} from '@polymer/decorators';
+import {HookApi, PluginApi} from '../gr-plugin-types';
+
+const INIT_PROPERTIES_TIMEOUT_MS = 10000;
+
+@customElement('gr-endpoint-decorator')
+class GrEndpointDecorator extends GestureEventListeners(
+ LegacyElementMixin(PolymerElement)
+) {
+ static get template() {
+ return htmlTemplate;
+ }
+
+ @property({type: String})
+ name!: string;
+
+ @property({type: Object})
+ _domHooks = new Map<HTMLElement, HookApi>();
+
+ @property({type: Object})
+ _initializedPlugins = new Map<string, boolean>();
+
+ /**
+ * This is the callback that the plugin endpoint manager should be calling
+ * when a new element is registered for this endpoint. It points to
+ * _initModule().
+ */
+ _endpointCallBack: (info: ModuleInfo) => void = () => {};
+
+ /** @override */
+ detached() {
+ super.detached();
+ for (const [el, domHook] of this._domHooks) {
+ domHook.handleInstanceDetached(el);
+ }
+ getPluginEndpoints().onDetachedEndpoint(this.name, this._endpointCallBack);
+ }
+
+ _initDecoration(
+ name: string,
+ plugin: PluginApi,
+ slot?: string
+ ): Promise<HTMLElement> {
+ const el = document.createElement(name);
+ return this._initProperties(
+ el,
+ plugin,
+ this.getContentChildren().find(el => el.nodeName !== 'GR-ENDPOINT-PARAM')
+ ).then(el => {
+ const slotEl = slot
+ ? this.querySelector(`gr-endpoint-slot[name=${slot}]`)
+ : null;
+ if (slot && slotEl?.parentNode) {
+ slotEl.parentNode.insertBefore(el, slotEl.nextSibling);
+ } else {
+ this._appendChild(el);
+ }
+ return el;
+ });
+ }
+
+ _initReplacement(name: string, plugin: PluginApi): Promise<HTMLElement> {
+ this.getContentChildNodes()
+ .filter(node => node.nodeName !== 'GR-ENDPOINT-PARAM')
+ .forEach(node => (node as ChildNode).remove());
+ const el = document.createElement(name);
+ return this._initProperties(el, plugin).then((el: HTMLElement) =>
+ this._appendChild(el)
+ );
+ }
+
+ _getEndpointParams() {
+ return Array.from(this.querySelectorAll('gr-endpoint-param'));
+ }
+
+ _initProperties(
+ htmlEl: HTMLElement,
+ plugin: PluginApi,
+ content?: HTMLElement
+ ) {
+ const el = htmlEl as HTMLElement & {
+ plugin?: PluginApi;
+ content?: HTMLElement;
+ };
+ el.plugin = plugin;
+ if (content) {
+ el.content = content;
+ }
+ const expectProperties = this._getEndpointParams().map(paramEl => {
+ const helper = plugin.attributeHelper(paramEl);
+ const paramName = paramEl.getAttribute('name');
+ if (!paramName) throw Error('plugin endpoint parameter missing a name');
+ return helper
+ .get('value')
+ .then(() =>
+ helper.bind('value', value =>
+ plugin.attributeHelper(el).set(paramName, value)
+ )
+ );
+ });
+ // TODO(TS): Should be a number, but TS thinks that is must be some weird
+ // NodeJS.Timeout object.
+ let timeoutId: any;
+ const timeout = new Promise(
+ () =>
+ (timeoutId = setTimeout(() => {
+ console.warn(
+ 'Timeout waiting for endpoint properties initialization: ' +
+ `plugin ${plugin.getPluginName()}, endpoint ${this.name}`
+ );
+ }, INIT_PROPERTIES_TIMEOUT_MS))
+ );
+ return Promise.race([timeout, Promise.all(expectProperties)])
+ .then(() => el)
+ .finally(() => {
+ if (timeoutId) clearTimeout(timeoutId);
+ });
+ }
+
+ _appendChild(el: HTMLElement): HTMLElement {
+ if (!this.root) throw Error('plugin endpoint decorator missing root');
+ return this.root.appendChild(el);
+ }
+
+ _initModule({moduleName, plugin, type, domHook, slot}: ModuleInfo) {
+ const name = plugin.getPluginName() + '.' + moduleName;
+ if (this._initializedPlugins.get(name)) {
+ return;
+ }
+ let initPromise;
+ switch (type) {
+ case 'decorate':
+ initPromise = this._initDecoration(moduleName, plugin, slot);
+ break;
+ case 'replace':
+ initPromise = this._initReplacement(moduleName, plugin);
+ break;
+ }
+ if (!initPromise) {
+ throw Error(`unknown endpoint type ${type} used by plugin ${name}`);
+ }
+ this._initializedPlugins.set(name, true);
+ initPromise.then(el => {
+ if (domHook) {
+ domHook.handleInstanceAttached(el);
+ this._domHooks.set(el, domHook);
+ }
+ });
+ }
+
+ /** @override */
+ ready() {
+ super.ready();
+ this._endpointCallBack = (info: ModuleInfo) => this._initModule(info);
+ getPluginEndpoints().onNewEndpoint(this.name, this._endpointCallBack);
+ if (this.name) {
+ getPluginLoader()
+ .awaitPluginsLoaded()
+ .then(() => getPluginEndpoints().getAndImportPlugins(this.name))
+ .then(() =>
+ getPluginEndpoints()
+ .getDetails(this.name)
+ .forEach(this._initModule, this)
+ );
+ }
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-endpoint-decorator': GrEndpointDecorator;
+ }
+}
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-types.ts b/polygerrit-ui/app/elements/plugins/gr-plugin-types.ts
index 7ac670b..538c78a 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-types.ts
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-types.ts
@@ -37,6 +37,8 @@
getAllAttached(): HTMLElement[];
getLastAttached(): Promise<HTMLElement>;
getModuleName(): string;
+ handleInstanceDetached(instance: HTMLElement): void;
+ handleInstanceAttached(instance: HTMLElement): void;
}
export enum TargetElement {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.ts
index 2aabf34..3935ef1 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-endpoints.ts
@@ -21,7 +21,7 @@
type Callback = (value: any) => void;
-interface ModuleInfo {
+export interface ModuleInfo {
moduleName: string;
plugin: PluginApi;
pluginUrl?: URL;
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.ts b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.ts
index aabbac1..4ac868d 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.ts
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.ts
@@ -63,7 +63,18 @@
if (this.focusableNodes) {
return this.focusableNodes;
}
- return super._focusableNodes;
+ // TODO(TS): to avoid ts error for:
+ // Only public and protected methods of the base class are accessible
+ // via the 'super' keyword.
+ // we call IronFocsablesHelper directly here
+ // Currently IronFocsablesHelper is not exported from iron-focusables-helper
+ // as it should so we use Polymer.IronFocsablesHelper here instead
+ // (can not use the IronFocsablesHelperClass
+ // in case different behavior due to singleton)
+ // once the type contains the exported member,
+ // should replace with:
+ // import {IronFocusablesHelper} from '@polymer/iron-overlay-behavior/iron-focusables-helper';
+ return (window.Polymer as any).IronFocusablesHelper.getTabbableNodes(this);
}
/** @override */
diff --git a/polygerrit-ui/app/scripts/bundled-polymer.js b/polygerrit-ui/app/scripts/bundled-polymer.js
index 780d82a..6fef454 100644
--- a/polygerrit-ui/app/scripts/bundled-polymer.js
+++ b/polygerrit-ui/app/scripts/bundled-polymer.js
@@ -66,6 +66,9 @@
import 'polymer-bridges/polymer/lib/elements/custom-style_bridge.js';
import 'polymer-bridges/polymer/lib/legacy/mutable-data-behavior_bridge.js';
import 'polymer-bridges/polymer/polymer-legacy_bridge.js';
+
+// This is needed due to the Polymer.IronFocusablesHelper in gr-overlay.ts
+import 'polymer-bridges/iron-overlay-behavior/iron-focusables-helper_bridge.js';
import {importHref} from './import-href.js';
window.Polymer.importHref = importHref;