Merge "Return old and new ChangeInfos in get-meta-diff"
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index 1453fd0..ee579ff 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -53,30 +53,30 @@
}
/**
+ * Represents a "generic" text range in the code (e.g. text selection)
+ */
+interface TextRange {
+ /** first line of the range (1-based inclusive). */
+ start_line: number;
+ /** first column of the range (in the first line) (1-based inclusive). */
+ start_column: number;
+ /** last line of the range (1-based inclusive). */
+ end_line: number;
+ /** last column of the range (in the end line) (1-based inclusive). */
+ end_column: number;
+}
+
+/**
* Represents a syntax block in a code (e.g. method, function, class, if-else).
*/
export declare interface SyntaxBlock {
/** Name of the block (e.g. name of the method/class)*/
name: string;
- /** Where does this block syntatically starts and ends (line number and column).*/
- range: {
- /** first line of the block (1-based inclusive). */
- start_line: number;
- /**
- * column of the range start inside the first line (e.g. "{" character ending a function/method)
- * (1-based inclusive).
- */
- start_column: number;
- /**
- * last line of the block (1-based inclusive).
- */
- end_line: number;
- /**
- * column of the block end inside the end line (e.g. "}" character ending a function/method)
- * (1-based inclusive).
- */
- end_column: number;
- };
+ /**
+ * Where does this block syntatically starts and ends (line number and
+ * column).
+ */
+ range: TextRange;
/** Sub-blocks of the current syntax block (e.g. methods of a class) */
children: SyntaxBlock[];
}
@@ -210,15 +210,22 @@
}
/**
- * Listens to changes in token highlighting - when a new token starts or stopped being highlighted.
- * Examples:
- * - Token highlighted: ('myFunctionName', 12, [Element]).
- * - Token unhighlighted: (undefined, 0, undefined).
+ * Event details when a token is highlighted.
*/
-export type TokenHighlightedListener = (
- newHighlight: string | undefined,
- newLineNumber: number,
- hoveredElement?: Element
+export declare interface TokenHighlightEventDetails {
+ token: string;
+ element: Element;
+ side: Side;
+ range: TextRange;
+}
+
+/**
+ * Listens to changes in token highlighting - when a new token starts or stopped
+ * being highlighted. undefined is sent if the event is about a clear in
+ * highlighting.
+ */
+export type TokenHighlightListener = (
+ tokenHighlightEvent?: TokenHighlightEventDetails
) => void;
export declare interface ImageDiffPreferences {
diff --git a/polygerrit-ui/app/api/embed.ts b/polygerrit-ui/app/api/embed.ts
index fed724e..520aeec 100644
--- a/polygerrit-ui/app/api/embed.ts
+++ b/polygerrit-ui/app/api/embed.ts
@@ -24,7 +24,7 @@
DiffLayer,
GrAnnotation,
GrDiffCursor,
- TokenHighlightedListener,
+ TokenHighlightListener,
} from './diff';
declare global {
@@ -35,7 +35,7 @@
TokenHighlightLayer: {
new (
container?: HTMLElement,
- listener?: TokenHighlightedListener
+ listener?: TokenHighlightListener
): DiffLayer;
};
};
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
index 941a09a..6605350 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
@@ -17,7 +17,6 @@
import '../../../styles/gr-table-styles';
import '../../../styles/shared-styles';
-import '../../shared/gr-date-formatter/gr-date-formatter';
import '../../shared/gr-account-link/gr-account-link';
import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-group-audit-log_html';
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
index 40c2f30..828aa55 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_html.ts
@@ -43,7 +43,7 @@
<template is="dom-repeat" items="[[_auditLog]]">
<tr class="table">
<td class="date">
- <gr-date-formatter has-tooltip="" date-str="[[item.date]]">
+ <gr-date-formatter withTooltip date-str="[[item.date]]">
</gr-date-formatter>
</td>
<td class="type">[[itemType(item.type)]]</td>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
index d26d14c..b91b04b 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
@@ -140,7 +140,7 @@
'https://test/site/group/url');
});
- test('save members correctly', () => {
+ test('save members correctly', async () => {
element._groupOwner = true;
const memberName = 'test-admin';
@@ -155,6 +155,7 @@
element.$.groupMemberSearchInput.text = memberName;
element.$.groupMemberSearchInput.value = 1234;
+ await flush();
assert.isFalse(button.hasAttribute('disabled'));
return element._handleSavingGroupMember().then(() => {
@@ -165,7 +166,7 @@
});
});
- test('save included groups correctly', () => {
+ test('save included groups correctly', async () => {
element._groupOwner = true;
const includedGroupName = 'testName';
@@ -179,7 +180,7 @@
element.$.includedGroupSearchInput.text = includedGroupName;
element.$.includedGroupSearchInput.value = 'testId';
-
+ await flush();
assert.isFalse(button.hasAttribute('disabled'));
return element._handleSavingIncludedGroups().then(() => {
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
index e492a15..e390ac5 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group_test.js
@@ -94,6 +94,7 @@
element.$.groupNameInput.text = groupName2;
+ await flush();
assert.isFalse(button.hasAttribute('disabled'));
assert.isTrue(element.$.groupName.classList.contains('edited'));
@@ -122,6 +123,7 @@
element.$.groupOwnerInput.text = 'testId2';
+ await flush();
assert.isFalse(button.hasAttribute('disabled'));
assert.isTrue(element.$.groupOwner.classList.contains('edited'));
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
index 4f66f0d..429a6d6 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_html.ts
@@ -145,10 +145,7 @@
<td class$="tagger [[_hideIfBranch(detailType)]]">
<div class$="tagger [[_computeHideTagger(item.tagger)]]">
<gr-account-link account="[[item.tagger]]"> </gr-account-link>
- (<gr-date-formatter
- has-tooltip=""
- date-str="[[item.tagger.date]]"
- >
+ (<gr-date-formatter withTooltip date-str="[[item.tagger.date]]">
</gr-date-formatter
>)
</div>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
index dad451b..f0d9268 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_html.ts
@@ -253,7 +253,7 @@
hidden$="[[_computeIsColumnHidden('Updated', visibleChangeTableColumns)]]"
>
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[_formatDate(change.updated)]]"
></gr-date-formatter>
</td>
@@ -262,7 +262,7 @@
hidden$="[[_computeIsColumnHidden('Submitted', visibleChangeTableColumns)]]"
>
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[_formatDate(change.submitted)]]"
></gr-date-formatter>
</td>
@@ -271,9 +271,9 @@
hidden$="[[_computeIsColumnHidden('Waiting', visibleChangeTableColumns)]]"
>
<gr-date-formatter
- has-tooltip=""
- force-relative=""
- relative-option-no-ago=""
+ withTooltip
+ forceRelative
+ relativeOptionNoAge
date-str="[[_computeWaiting(account, change)]]"
></gr-date-formatter>
</td>
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.ts b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.ts
index beadea3..9242a58 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.ts
@@ -15,7 +15,6 @@
* limitations under the License.
*/
-import '../../shared/gr-date-formatter/gr-date-formatter';
import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {RepoName} from '../../../types/common';
import {WebLinkInfo} from '../../../types/diff';
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
index 614339a..b5f55ca 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_html.ts
@@ -84,24 +84,27 @@
hidden$="[[_shouldHideActions(_topLevelActions.*, _loading)]]"
>
<template is="dom-repeat" items="[[_topLevelPrimaryActions]]" as="action">
- <gr-button
- link=""
+ <gr-tooltip-content
title$="[[action.title]]"
has-tooltip="[[_computeHasTooltip(action.title)]]"
position-below="true"
- data-action-key$="[[action.__key]]"
- class$="[[action.__key]]"
- data-action-type$="[[action.__type]]"
- data-label$="[[action.label]]"
- disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
- on-click="_handleActionTap"
>
- <iron-icon
- class$="[[_computeHasIcon(action)]]"
- icon$="gr-icons:[[action.icon]]"
- ></iron-icon>
- [[action.label]]
- </gr-button>
+ <gr-button
+ link=""
+ data-action-key$="[[action.__key]]"
+ class$="[[action.__key]]"
+ data-action-type$="[[action.__type]]"
+ data-label$="[[action.label]]"
+ disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
+ on-click="_handleActionTap"
+ >
+ <iron-icon
+ class$="[[_computeHasIcon(action)]]"
+ icon$="gr-icons:[[action.icon]]"
+ ></iron-icon>
+ [[action.label]]
+ </gr-button>
+ </gr-tooltip-content>
</template>
</section>
<section
@@ -113,24 +116,27 @@
items="[[_topLevelSecondaryActions]]"
as="action"
>
- <gr-button
- link=""
+ <gr-tooltip-content
title$="[[action.title]]"
has-tooltip="[[_computeHasTooltip(action.title)]]"
position-below="true"
- data-action-key$="[[action.__key]]"
- class$="[[action.__key]]"
- data-action-type$="[[action.__type]]"
- data-label$="[[action.label]]"
- disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
- on-click="_handleActionTap"
>
- <iron-icon
- class$="[[_computeHasIcon(action)]]"
- icon$="gr-icons:[[action.icon]]"
- ></iron-icon>
- [[action.label]]
- </gr-button>
+ <gr-button
+ link=""
+ data-action-key$="[[action.__key]]"
+ class$="[[action.__key]]"
+ data-action-type$="[[action.__type]]"
+ data-label$="[[action.label]]"
+ disabled$="[[_calculateDisabled(action, _hasKnownChainState)]]"
+ on-click="_handleActionTap"
+ >
+ <iron-icon
+ class$="[[_computeHasIcon(action)]]"
+ icon$="gr-icons:[[action.icon]]"
+ ></iron-icon>
+ [[action.label]]
+ </gr-button>
+ </gr-tooltip-content>
</template>
</section>
<gr-button hidden$="[[!_loading]]" disabled=""
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
index c080345..c105aaa 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
@@ -134,9 +134,9 @@
<span class="title">Submitted</span>
<span class="value">
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[change.submitted]]"
- show-yesterday=""
+ showYesterday=""
></gr-date-formatter>
</span>
</section>
@@ -154,9 +154,9 @@
</span>
<span class="value">
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[change.updated]]"
- show-yesterday=""
+ showYesterday
></gr-date-formatter>
</span>
</section>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index ced66a9..e940e5a 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -24,7 +24,6 @@
import '../../shared/gr-button/gr-button';
import '../../shared/gr-change-star/gr-change-star';
import '../../shared/gr-change-status/gr-change-status';
-import '../../shared/gr-date-formatter/gr-date-formatter';
import '../../shared/gr-editable-content/gr-editable-content';
import '../../shared/gr-linked-text/gr-linked-text';
import '../../shared/gr-overlay/gr-overlay';
@@ -52,7 +51,7 @@
} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import {GrEditConstants} from '../../edit/gr-edit-constants';
import {pluralize} from '../../../utils/string-util';
-import {windowLocationReload} from '../../../utils/dom-util';
+import {windowLocationReload, querySelectorAll} from '../../../utils/dom-util';
import {
GeneratedWebLink,
GerritNav,
@@ -1173,6 +1172,9 @@
_paramsChanged(value: AppElementChangeViewParams) {
if (value.view !== GerritView.CHANGE) {
this._initialLoadComplete = false;
+ querySelectorAll(this, 'gr-overlay').forEach(overlay =>
+ (overlay as GrOverlay).close()
+ );
return;
}
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
index ff078f0..1256cc1 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.js
@@ -115,10 +115,10 @@
current_revision: 'a',
},
];
- setup(() => {
+ setup(async () => {
element.updateChanges(changes);
element._cherryPickType = CHERRY_PICK_TYPES.TOPIC;
- flush();
+ await flush();
});
test('cherry pick topic submit', async () => {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
index bceee27..91f7e46 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header_html.ts
@@ -180,50 +180,61 @@
hidden$="[[_computePrefsButtonHidden(diffPrefs, diffPrefsDisabled)]]"
hidden=""
>
- <gr-button
- link=""
+ <gr-tooltip-content
has-tooltip=""
title="Diff preferences"
- class="prefsButton desktop"
- on-click="_handlePrefsTap"
- ><iron-icon icon="gr-icons:settings"></iron-icon
- ></gr-button>
+ >
+ <gr-button
+ link=""
+
+ class="prefsButton desktop"
+ on-click="_handlePrefsTap"
+ ><iron-icon icon="gr-icons:settings"></iron-icon
+ ></gr-button>
+ </gr-tooltip-content>
</span>
<span class="separator"></span>
</div>
<span class="downloadContainer desktop">
- <gr-button
- link=""
- class="download"
- title="[[createTitle(Shortcut.OPEN_DOWNLOAD_DIALOG,
- ShortcutSection.ACTIONS)]]"
+ <gr-tooltip-content
has-tooltip=""
- on-click="_handleDownloadTap"
- >Download</gr-button
+ title="[[createTitle(Shortcut.OPEN_DOWNLOAD_DIALOG,
+ ShortcutSection.ACTIONS)]]"
>
+ <gr-button
+ link=""
+ class="download"
+ on-click="_handleDownloadTap"
+ >Download</gr-button
+ >
+ </gr-tooltip-content>
</span>
<template
is="dom-if"
if="[[_fileListActionsVisible(shownFileCount, _maxFilesForBulkActions)]]"
>
- <gr-button
- id="expandBtn"
- link=""
- title="[[createTitle(Shortcut.TOGGLE_ALL_INLINE_DIFFS,
- ShortcutSection.FILE_LIST)]]"
- has-tooltip=""
- on-click="_expandAllDiffs"
- >Expand All</gr-button
- >
- <gr-button
- id="collapseBtn"
- link=""
- on-click="_collapseAllDiffs"
- title="[[createTitle(Shortcut.TOGGLE_ALL_INLINE_DIFFS,
- ShortcutSection.FILE_LIST)]]"
- has-tooltip=""
- >Collapse All</gr-button
- >
+ <gr-tooltip-content
+ has-tooltip=""
+ title="[[createTitle(Shortcut.TOGGLE_ALL_INLINE_DIFFS,
+ ShortcutSection.FILE_LIST)]]">
+ <gr-button
+ id="expandBtn"
+ link=""
+
+ on-click="_expandAllDiffs"
+ >Expand All</gr-button
+ >
+ <gr-tooltip-content
+ has-tooltip=""
+ title="[[createTitle(Shortcut.TOGGLE_ALL_INLINE_DIFFS,
+ ShortcutSection.FILE_LIST)]]">
+ <gr-button
+ id="collapseBtn"
+ link=""
+ on-click="_collapseAllDiffs"
+ >Collapse All</gr-button
+ >
+ </gr-tooltip-content>
</template>
<template
is="dom-if"
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
index 5def25a..4acf245 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
@@ -17,6 +17,7 @@
import '../../../test/common-test-setup-karma.js';
import '../../diff/gr-comment-api/gr-comment-api.js';
+import '../../shared/gr-date-formatter/gr-date-formatter.js';
import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
import './gr-file-list.js';
import {createCommentApiMockWithTemplateElement} from '../../../test/mocks/comment-api.js';
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
index f9d21d9..618403c 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_html.ts
@@ -53,25 +53,27 @@
);
padding: 0 var(--spacing-m);
}
- gr-button.iron-selected[vote='max'] {
+ gr-tooltip-content.iron-selected > gr-button[vote='max'] {
--button-background-color: var(--vote-color-approved);
}
- gr-button.iron-selected[vote='positive'] {
+ gr-tooltip-content.iron-selected > gr-buttonvote='positive'] {
--button-background-color: var(--vote-color-recommended);
}
- gr-button.iron-selected[vote='min'] {
+ gr-tooltip-content.iron-selected > gr-button[vote='min'] {
--button-background-color: var(--vote-color-rejected);
}
- gr-button.iron-selected[vote='negative'] {
+ gr-tooltip-content.iron-selected > gr-button[vote='negative'] {
--button-background-color: var(--vote-color-disliked);
}
- gr-button.iron-selected[vote='neutral'] {
+ gr-tooltip-content.iron-selected > gr-button[vote='neutral'] {
--button-background-color: var(--vote-color-neutral);
}
- gr-button.iron-selected[vote='positive']::part(paper-button) {
+ gr-tooltip-content.iron-selected
+ > gr-button[vote='positive']::part(paper-button) {
border-color: var(--vote-outline-recommended);
}
- gr-button.iron-selected[vote='negative']::part(paper-button) {
+ gr-tooltip-content.iron-selected
+ > gr-button[vote='negative']::part(paper-button) {
border-color: var(--vote-outline-disliked);
}
.placeholder {
@@ -116,17 +118,20 @@
aria-labelledby="labelName"
>
<template is="dom-repeat" items="[[_items]]" as="value">
- <gr-button
- role="radio"
- vote$="[[_computeVoteAttribute(value, index, _items.length)]]"
- vote-chip
+ <gr-tooltip-content
has-tooltip=""
+ title$="[[_computeLabelValueTitle(labels, label.name, value)]]"
data-name$="[[label.name]]"
data-value$="[[value]]"
- title$="[[_computeLabelValueTitle(labels, label.name, value)]]"
>
- [[value]]</gr-button
- >
+ <gr-button
+ role="radio"
+ vote="[[_computeVoteAttribute(value, index, _items.length)]]"
+ voteChip
+ >
+ [[value]]
+ </gr-button>
+ </gr-tooltip-content>
</template>
</iron-selector>
<template
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
index e7ff236..4e66b4e 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.js
@@ -97,14 +97,14 @@
}
}
- test('label picker', () => {
+ test('label picker', async () => {
const labelsChangedHandler = sinon.stub();
element.addEventListener('labels-changed', labelsChangedHandler);
assert.ok(element.$.labelSelector);
MockInteractions.tap(element.shadowRoot
.querySelector(
- 'gr-button[data-value="-1"]'));
- flush();
+ 'gr-tooltip-content[data-value="-1"] > gr-button'));
+ await flush();
assert.strictEqual(element.selectedValue, '-1');
assert.strictEqual(element.selectedItem
.textContent.trim(), '-1');
@@ -160,24 +160,28 @@
checkAriaCheckedValid();
});
- test('do not display tooltips on touch devices', () => {
- const verifiedBtn = element.shadowRoot
- .querySelector(
- 'iron-selector > gr-button[data-value="-1"]');
+ test('do not display tooltips on touch devices', async () => {
+ const verifiedTooltip = element.shadowRoot
+ .querySelector('iron-selector > gr-tooltip-content');
// On touch devices, tooltips should not be shown.
- verifiedBtn._isTouchDevice = true;
- verifiedBtn._handleShowTooltip();
- assert.isNotOk(verifiedBtn._tooltip);
- verifiedBtn._handleHideTooltip();
- assert.isNotOk(verifiedBtn._tooltip);
+ verifiedTooltip._isTouchDevice = true;
+ await flush();
+ verifiedTooltip._handleShowTooltip();
+ await flush();
+ assert.isNotOk(verifiedTooltip._tooltip);
+ verifiedTooltip._handleHideTooltip();
+ await flush();
+ assert.isNotOk(verifiedTooltip._tooltip);
// On other devices, tooltips should be shown.
- verifiedBtn._isTouchDevice = false;
- verifiedBtn._handleShowTooltip();
- assert.isOk(verifiedBtn._tooltip);
- verifiedBtn._handleHideTooltip();
- assert.isNotOk(verifiedBtn._tooltip);
+ verifiedTooltip._isTouchDevice = false;
+ verifiedTooltip._handleShowTooltip();
+ await flush();
+ assert.isOk(verifiedTooltip._tooltip);
+ verifiedTooltip._handleHideTooltip();
+ await flush();
+ assert.isNotOk(verifiedTooltip._tooltip);
});
test('_computeLabelValue', () => {
@@ -209,7 +213,7 @@
'Code-Review'), []);
});
- test('changes in label score are reflected in the DOM', () => {
+ test('changes in label score are reflected in the DOM', async () => {
element.labels = {
'Code-Review': {
values: {
@@ -232,9 +236,10 @@
default_value: 0,
},
};
+ await flush();
const selector = element.$.labelSelector;
element.set('label', {name: 'Verified', value: ' 0'});
- flush();
+ await flush();
assert.strictEqual(selector.selected, ' 0');
assert.strictEqual(
element.$.selectedValueLabel.textContent.trim(), 'No score');
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
index cfecf91..f529464 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.ts
@@ -85,7 +85,7 @@
await flush();
});
- test('get and set label scores', () => {
+ test('get and set label scores', async () => {
for (const label of Object.keys(element.permittedLabels!)) {
const row = queryAndAssert<GrLabelScoreRow>(
element,
@@ -93,6 +93,7 @@
);
row.setSelectedValue('-1');
}
+ await flush();
assert.deepEqual(element.getLabelValues(), {
'Code-Review': -1,
Verified: -1,
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
index ddbd22e..c9680ef 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
@@ -325,8 +325,8 @@
<template is="dom-if" if="[[!message.id]]">
<span class="date">
<gr-date-formatter
- has-tooltip=""
- show-date-and-time=""
+ withTooltip
+ showDateAndTime
date-str="[[message.date]]"
></gr-date-formatter>
</span>
@@ -334,8 +334,8 @@
<template is="dom-if" if="[[message.id]]">
<span class="date" on-click="_handleAnchorClick">
<gr-date-formatter
- has-tooltip=""
- show-date-and-time=""
+ withTooltip
+ showDateAndTime
date-str="[[message.date]]"
></gr-date-formatter>
</span>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
index 933cb821..b8c9319 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.js
@@ -127,7 +127,7 @@
const labelScoreRows = element.getLabelScores().shadowRoot
.querySelector('gr-label-score-row[name="Code-Review"]');
const selectedBtn = labelScoreRows.shadowRoot
- .querySelector('gr-button[data-value="+1"].iron-selected');
+ .querySelector('gr-tooltip-content[data-value="+1"] > gr-button');
assert.isOk(selectedBtn);
});
});
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
index b571985..3fedcd9 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_html.ts
@@ -441,23 +441,26 @@
></gr-account-label>
</template>
</template>
- <gr-button
- class="edit-attention-button"
- on-click="_handleAttentionModify"
- disabled="[[_sendDisabled]]"
- link=""
- position-below=""
- data-label="Edit"
- data-action-type="change"
- data-action-key="edit"
+ <gr-tooltip-content
has-tooltip=""
title="[[_computeAttentionButtonTitle(_sendDisabled)]]"
- role="button"
- tabindex="0"
>
- <iron-icon icon="gr-icons:edit"></iron-icon>
- Modify
- </gr-button>
+ <gr-button
+ class="edit-attention-button"
+ on-click="_handleAttentionModify"
+ disabled="[[_sendDisabled]]"
+ link=""
+ position-below=""
+ data-label="Edit"
+ data-action-type="change"
+ data-action-key="edit"
+ role="button"
+ tabindex="0"
+ >
+ <iron-icon icon="gr-icons:edit"></iron-icon>
+ Modify
+ </gr-button>
+ </gr-tooltip-content>
</div>
<div>
<a
@@ -612,26 +615,32 @@
<!-- Use 'Send' here as the change may only about reviewers / ccs
and when this button is visible, the next button will always
be 'Start review' -->
- <gr-button
- link=""
- disabled="[[_isState(knownLatestState, 'not-latest')]]"
- class="action save"
+ <gr-tooltip-content
has-tooltip=""
- title="[[_saveTooltip]]"
- on-click="_saveClickHandler"
- >Send As WIP</gr-button
+ title$="[[_saveTooltip]]"
>
+ <gr-button
+ link=""
+ disabled="[[_isState(knownLatestState, 'not-latest')]]"
+ class="action save"
+ on-click="_saveClickHandler"
+ >Send As WIP</gr-button
+ >
+ </gr-tooltip-content>
</template>
- <gr-button
- id="sendButton"
- primary=""
- disabled="[[_sendDisabled]]"
- class="action send"
+ <gr-tooltip-content
has-tooltip=""
title$="[[_computeSendButtonTooltip(canBeStarted, _commentEditing)]]"
- on-click="_sendTapHandler"
- >[[_sendButtonLabel]]</gr-button
>
+ <gr-button
+ id="sendButton"
+ primary=""
+ disabled="[[_sendDisabled]]"
+ class="action send"
+ on-click="_sendTapHandler"
+ >[[_sendButtonLabel]]
+ </gr-button>
+ </gr-tooltip-content>
</div>
</section>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
index e3bbd9e..e57ffc7 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.ts
@@ -116,7 +116,7 @@
return {id: `${lastId++}` as GroupId};
};
- setup(() => {
+ setup(async () => {
changeNum = 42 as NumericChangeId;
patchNum = 1 as PatchSetNum;
@@ -168,7 +168,7 @@
// .returns(Promise.resolve({isLatest: true}));
// Allow the elements created by dom-repeat to be stamped.
- flush();
+ await flush();
});
function stubSaveReview(
@@ -216,6 +216,7 @@
// which the dom-repeat elements are stamped.
await flush();
tap(queryAndAssert(element, '.send'));
+ await flush();
const review = await saveReviewPromise;
assert.deepEqual(review, {
@@ -1063,6 +1064,7 @@
const label = 'Verified';
const value = '+1';
element.setLabelValue(label, value);
+ await flush();
const labels = (
queryAndAssert(element, '#labelScores') as GrLabelScores
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index 28354d2..c197599 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -303,9 +303,6 @@
renderStatusLink() {
const link = this.run.statusLink;
if (!link) return;
- // For COMPLETED we think that the status link are too much clutter.
- // That could be re-considered.
- if (this.run.status !== RunStatus.RUNNING) return;
return html`
<a href="${link}" target="_blank" @click="${this.onLinkClick}"
><iron-icon
@@ -604,42 +601,48 @@
@click="${() => fireRunSelectionReset(this)}"
>Unselect All</gr-button
>
- <gr-button
- class="font-normal"
- link
+ <gr-tooltip-content
title="${runButtonDisabled
? 'Disabled. Unselect checks without a "Run" action to enable the button.'
: ''}"
has-tooltip="${runButtonDisabled}"
- ?disabled="${runButtonDisabled}"
- @click="${() => {
- actions.forEach(action => this.checksService.triggerAction(action));
- }}"
- >Run Selected</gr-button
>
+ <gr-button
+ class="font-normal"
+ link
+ ?disabled="${runButtonDisabled}"
+ @click="${() => {
+ actions.forEach(action => this.checksService.triggerAction(action));
+ }}"
+ >Run Selected</gr-button
+ >
+ </gr-tooltip-content>
`;
}
private renderCollapseButton() {
return html`
- <gr-button
- link
- class="expandButton"
- role="switch"
- ?aria-checked="${this.collapsed}"
- aria-label="${this.collapsed
- ? 'Expand runs panel'
- : 'Collapse runs panel'}"
+ <gr-tooltip-content
has-tooltip="true"
title="${this.collapsed ? 'Expand runs panel' : 'Collapse runs panel'}"
- @click="${() => (this.collapsed = !this.collapsed)}"
- ><iron-icon
- class="expandIcon"
- icon="${this.collapsed
- ? 'gr-icons:chevron-right'
- : 'gr-icons:chevron-left'}"
- ></iron-icon>
- </gr-button>
+ >
+ <gr-button
+ link
+ class="expandButton"
+ role="switch"
+ ?aria-checked="${this.collapsed}"
+ aria-label="${this.collapsed
+ ? 'Expand runs panel'
+ : 'Collapse runs panel'}"
+ @click="${() => (this.collapsed = !this.collapsed)}"
+ ><iron-icon
+ class="expandIcon"
+ icon="${this.collapsed
+ ? 'gr-icons:chevron-right'
+ : 'gr-icons:chevron-left'}"
+ ></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>
`;
}
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
index d3d7615..94d37f5 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.ts
@@ -186,6 +186,7 @@
})
);
element._isApplyFixLoading = true;
+ await flush();
const button = getConfirmButton();
assert.isTrue(button.hasAttribute('disabled'));
assert.equal(button.getAttribute('title'), '');
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
index 480e26c..de7d007 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
@@ -15,9 +15,17 @@
* limitations under the License.
*/
import {DiffLayer, DiffLayerListener} from '../../../types/types';
-import {GrDiffLine, Side, TokenHighlightedListener} from '../../../api/diff';
+import {GrDiffLine, Side, TokenHighlightListener} from '../../../api/diff';
+import {assertIsDefined} from '../../../utils/common-util';
import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
import {debounce, DelayedTask} from '../../../utils/async-util';
+
+import {
+ getLineElByChild,
+ getSideByLineEl,
+ getPreviousContentNodes,
+} from '../gr-diff/gr-diff-utils';
+
import {
getLineNumberByChild,
lineNumberToNumber,
@@ -66,7 +74,7 @@
private currentHighlight?: string;
/** Trigger when a new token starts or stoped being highlighted.*/
- private readonly tokenHighlightedListener?: TokenHighlightedListener;
+ private readonly tokenHighlightListener?: TokenHighlightListener;
/**
* The line of the currently highlighted token. We store this in order to
@@ -100,9 +108,9 @@
constructor(
container: HTMLElement = document.documentElement,
- tokenHighlightedListener?: TokenHighlightedListener
+ tokenHighlightListener?: TokenHighlightListener
) {
- this.tokenHighlightedListener = tokenHighlightedListener;
+ this.tokenHighlightListener = tokenHighlightListener;
container.addEventListener('click', e => {
this.handleContainerClick(e);
});
@@ -260,18 +268,42 @@
const oldLineNumber = this.currentHighlightLineNumber;
this.currentHighlight = newHighlight;
this.currentHighlightLineNumber = newLineNumber;
-
- if (this.tokenHighlightedListener) {
- this.tokenHighlightedListener(
- newHighlight,
- newLineNumber,
- newHoveredElement
- );
- }
+ this.triggerTokenHighlightEvent(
+ newHighlight,
+ newLineNumber,
+ newHoveredElement
+ );
this.notifyForToken(oldHighlight, oldLineNumber);
this.notifyForToken(newHighlight, newLineNumber);
}
+ triggerTokenHighlightEvent(
+ token: string | undefined,
+ line: number,
+ element: Element | undefined
+ ) {
+ if (!this.tokenHighlightListener) {
+ return;
+ }
+ if (!token || !element) {
+ this.tokenHighlightListener(undefined);
+ return;
+ }
+ const previousTextLength = getPreviousContentNodes(element)
+ .map(sib => sib.textContent!.length)
+ .reduce((partial_sum, a) => partial_sum + a, 0);
+ const lineEl = getLineElByChild(element);
+ assertIsDefined(lineEl, 'Line element should be found!');
+ const side = getSideByLineEl(lineEl);
+ const range = {
+ start_line: line,
+ start_column: previousTextLength + 1, // 1-based inclusive
+ end_line: line,
+ end_column: previousTextLength + token.length, // 1-based inclusive
+ };
+ this.tokenHighlightListener({token, element, side, range});
+ }
+
getSortedLinesForSide(
lineMapping: Map<string, Set<number>>,
token: string | undefined,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
index 4f44665..2993d35 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
@@ -17,7 +17,7 @@
import '../../../test/common-test-setup-karma';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
-import {Side} from '../../../api/diff';
+import {Side, TokenHighlightEventDetails} from '../../../api/diff';
import {GrDiffLine, GrDiffLineType} from '../gr-diff/gr-diff-line.js';
import {HOVER_DELAY_MS, TokenHighlightLayer} from './token-highlight-layer';
import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
@@ -66,14 +66,12 @@
let container: HTMLElement;
let listener: MockListener;
let highlighter: TokenHighlightLayer;
- let tokenHighlightingCalls: any[] = [];
+ let tokenHighlightingCalls: {details?: TokenHighlightEventDetails}[] = [];
- function tokenHighlightedListener(
- newHighlight: string | undefined,
- newLineNumber: number,
- hoveredElement?: Element
+ function tokenHighlightListener(
+ highlightDetails?: TokenHighlightEventDetails
) {
- tokenHighlightingCalls.push({newHighlight, newLineNumber, hoveredElement});
+ tokenHighlightingCalls.push({details: highlightDetails});
}
setup(async () => {
@@ -81,7 +79,7 @@
tokenHighlightingCalls = [];
container = document.createElement('div');
document.body.appendChild(container);
- highlighter = new TokenHighlightLayer(container, tokenHighlightedListener);
+ highlighter = new TokenHighlightLayer(container, tokenHighlightListener);
highlighter.addListener((...args) => listener.notify(...args));
});
@@ -107,10 +105,13 @@
const lineId = createLineId();
const template = html`
<div class="line">
- <div data-value=${line} class="lineNum"></div>
- <div id=${lineId} class="line-content">${text}</div>
+ <div data-value=${line} class="lineNum right"></div>
+ <div class="content">
+ <div id=${lineId} class="contentText">${text}</div>
+ </div>
</div>
`;
+
const div = document.createElement('div');
render(template, div);
container.appendChild(div);
@@ -277,19 +278,16 @@
assert.equal(tokenHighlightingCalls.length, 0);
clock.tick(HOVER_DELAY_MS);
assert.equal(tokenHighlightingCalls.length, 1);
- assert.deepEqual(tokenHighlightingCalls[0], {
- newHighlight: 'words',
- newLineNumber: 1,
- hoveredElement: words1,
+ assert.deepEqual(tokenHighlightingCalls[0].details, {
+ token: 'words',
+ side: Side.RIGHT,
+ element: words1,
+ range: {start_line: 1, start_column: 5, end_line: 1, end_column: 9},
});
MockInteractions.click(container);
assert.equal(tokenHighlightingCalls.length, 2);
- assert.deepEqual(tokenHighlightingCalls[1], {
- newHighlight: undefined,
- newLineNumber: 0,
- hoveredElement: undefined,
- });
+ assert.deepEqual(tokenHighlightingCalls[1].details, undefined);
});
test('clicking clears highlight', async () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
index 4e2b6a1..8a6d95d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector_html.ts
@@ -30,28 +30,34 @@
width: 1.3rem;
}
</style>
- <gr-button
- id="sideBySideBtn"
- link=""
+ <gr-tooltip-content
has-tooltip=""
- position-below="[[showTooltipBelow]]"
- class$="[[_computeSideBySideSelected(mode)]]"
title="Side-by-side diff"
- aria-pressed$="[[isSideBySideSelected(mode)]]"
- on-click="_handleSideBySideTap"
+ position-below="[[showTooltipBelow]]"
>
- <iron-icon icon="gr-icons:side-by-side"></iron-icon>
- </gr-button>
- <gr-button
- id="unifiedBtn"
- link=""
+ <gr-button
+ id="sideBySideBtn"
+ link=""
+ class$="[[_computeSideBySideSelected(mode)]]"
+ aria-pressed$="[[isSideBySideSelected(mode)]]"
+ on-click="_handleSideBySideTap"
+ >
+ <iron-icon icon="gr-icons:side-by-side"></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>
+ <gr-tooltip-content
has-tooltip=""
position-below="[[showTooltipBelow]]"
title="Unified diff"
- class$="[[_computeUnifiedSelected(mode)]]"
- aria-pressed$="[[isUnifiedSelected(mode)]]"
- on-click="_handleUnifiedTap"
>
- <iron-icon icon="gr-icons:unified"></iron-icon>
- </gr-button>
+ <gr-button
+ id="unifiedBtn"
+ link=""
+ class$="[[_computeUnifiedSelected(mode)]]"
+ aria-pressed$="[[isUnifiedSelected(mode)]]"
+ on-click="_handleUnifiedTap"
+ >
+ <iron-icon icon="gr-icons:unified"></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>
`;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
index 3f99790..4f1047f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
@@ -354,15 +354,15 @@
hidden=""
>
<span class="preferences desktop">
- <gr-button
- link=""
- class="prefsButton"
+ <gr-tooltip-content
has-tooltip=""
position-below=""
title="Diff preferences"
- on-click="_handlePrefsTap"
- ><iron-icon icon="gr-icons:settings"></iron-icon
- ></gr-button>
+ >
+ <gr-button link="" class="prefsButton" on-click="_handlePrefsTap"
+ ><iron-icon icon="gr-icons:settings"></iron-icon
+ ></gr-button>
+ </gr-tooltip-content>
</span>
</span>
<gr-endpoint-decorator name="annotation-toggler">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
index fada9cb..7393606 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
@@ -129,6 +129,29 @@
rootId: string;
}
+const VISIBLE_TEXT_NODE_TYPES = [Node.TEXT_NODE, Node.ELEMENT_NODE];
+
+export function getPreviousContentNodes(node?: Node | null) {
+ const sibs = [];
+ while (node) {
+ const {parentNode, previousSibling} = node;
+ const topContentLevel =
+ parentNode &&
+ (parentNode as HTMLElement).classList.contains('contentText');
+ let previousEl: Node | undefined | null;
+ if (previousSibling) {
+ previousEl = previousSibling;
+ } else if (!topContentLevel) {
+ previousEl = parentNode?.previousSibling;
+ }
+ if (previousEl && VISIBLE_TEXT_NODE_TYPES.includes(previousEl.nodeType)) {
+ sibs.push(previousEl);
+ }
+ node = previousEl;
+ }
+ return sibs;
+}
+
export function isThreadEl(node: Node): node is GrDiffThreadElement {
return (
node.nodeType === Node.ELEMENT_NODE &&
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
index 514f00e..51259c8 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_html.ts
@@ -56,7 +56,7 @@
<span class="title">Registered</span>
<span class="value">
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[_account.registered_on]]"
></gr-date-formatter>
</span>
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.ts b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.ts
index 2abedf1..96b1ded 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.ts
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.ts
@@ -15,7 +15,6 @@
* limitations under the License.
*/
import '../../shared/gr-button/gr-button';
-import '../../shared/gr-date-formatter/gr-date-formatter';
import '../../../styles/shared-styles';
import '../../../styles/gr-form-styles';
import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.ts b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.ts
index ace1e1a..4096b02 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.ts
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.ts
@@ -16,7 +16,6 @@
*/
import '@polymer/iron-input/iron-input';
import '../../shared/gr-button/gr-button';
-import '../../shared/gr-date-formatter/gr-date-formatter';
import '../../../styles/shared-styles';
import '../../../styles/gr-form-styles';
import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
index 61498f6..94333c7 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
@@ -29,7 +29,6 @@
import '../gr-change-table-editor/gr-change-table-editor';
import '../../shared/gr-button/gr-button';
import {GrButton} from '../../shared/gr-button/gr-button';
-import '../../shared/gr-date-formatter/gr-date-formatter';
import '../../shared/gr-diff-preferences/gr-diff-preferences';
import '../../shared/gr-page-nav/gr-page-nav';
import '../../shared/gr-select/gr-select';
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
index f746c29..9897a9f 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
@@ -206,18 +206,7 @@
></gr-hovercard-account>`
: ''}
${hasAttention
- ? html`<gr-button
- id="attentionButton"
- link=""
- aria-label="Remove user from attention set"
- @click=${this._handleRemoveAttentionClick}
- ?disabled=${!this._computeAttentionButtonEnabled(
- highlightAttention,
- account,
- change,
- this.selected,
- this._selfAccount
- )}
+ ? html` <gr-tooltip-content
?has-tooltip=${this._computeAttentionButtonEnabled(
highlightAttention,
account,
@@ -233,11 +222,25 @@
this.selected,
this._selfAccount
)}"
- ><iron-icon
- class="attention"
- icon="gr-icons:attention"
- ></iron-icon>
- </gr-button>`
+ >
+ <gr-button
+ id="attentionButton"
+ link=""
+ aria-label="Remove user from attention set"
+ @click=${this._handleRemoveAttentionClick}
+ ?disabled=${!this._computeAttentionButtonEnabled(
+ highlightAttention,
+ account,
+ change,
+ this.selected,
+ this._selfAccount
+ )}
+ ><iron-icon
+ class="attention"
+ icon="gr-icons:attention"
+ ></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>`
: ''}
</span>
<span
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
index 1ece10a..f7ebd66 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.ts
@@ -15,14 +15,11 @@
* limitations under the License.
*/
import '@polymer/paper-button/paper-button';
-import '../../../styles/shared-styles';
-import '../../../styles/gr-voting-styles';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {customElement, property, computed, observe} from '@polymer/decorators';
-import {htmlTemplate} from './gr-button_html';
-import {TooltipMixin} from '../../../mixins/gr-tooltip-mixin/gr-tooltip-mixin';
+import {spinnerStyles} from '../../../styles/gr-spinner-styles';
+import {votingStyles} from '../../../styles/gr-voting-styles';
+import {css, html, LitElement, PropertyValues} from 'lit';
+import {customElement, property} from 'lit/decorators';
import {
- PolymerEvent,
getEventPath,
getKeyboardEvent,
isModifierPressed,
@@ -37,87 +34,243 @@
}
}
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = TooltipMixin(PolymerElement);
-
@customElement('gr-button')
-export class GrButton extends base {
- static get template() {
- return htmlTemplate;
- }
+export class GrButton extends LitElement {
+ private readonly reporting: ReportingService = appContext.reportingService;
/**
* Should this button be rendered as a vote chip? Then we are applying
* the .voteChip class (see gr-voting-styles) to the paper-button.
*/
- @property({type: Boolean, reflectToAttribute: true})
+ @property({type: Boolean, reflect: true})
voteChip = false;
- @property({type: Boolean, reflectToAttribute: true})
- downArrow = false;
-
- @property({type: Boolean, reflectToAttribute: true})
- link = false;
-
- @property({type: Boolean})
- noUppercase = false;
-
- @property({type: Boolean, reflectToAttribute: true})
- loading = false;
-
- @property({type: Boolean, reflectToAttribute: true})
- disabled: boolean | null = null;
-
- @property({type: String})
- tooltip = '';
-
// Note: don't assign a value to this, since constructor is called
// after created, the initial value maybe overridden by this
- @property({type: String})
- _initialTabindex?: string;
+ private initialTabindex?: string;
- @computed('disabled', 'loading')
- get _disabled() {
- return this.disabled || this.loading;
+ @property({type: Boolean, reflect: true})
+ downArrow = false;
+
+ @property({type: Boolean, reflect: true})
+ link = false;
+
+ @property({type: Boolean, reflect: true})
+ loading = false;
+
+ @property({type: Boolean, reflect: true})
+ disabled: boolean | null = null;
+
+ static override get styles() {
+ return [
+ votingStyles,
+ spinnerStyles,
+ css`
+ /* general styles for all buttons */
+ :host {
+ --background-color: var(
+ --button-background-color,
+ var(--default-button-background-color)
+ );
+ --text-color: var(--default-button-text-color);
+ display: inline-block;
+ position: relative;
+ }
+ :host([hidden]) {
+ display: none;
+ }
+ :host([no-uppercase]) paper-button {
+ text-transform: none;
+ }
+ paper-button {
+ /* The next lines contains a copy of paper-button style.
+ Without a copy, the @apply works incorrectly with Polymer 2.
+ @apply is deprecated and is not recommended to use. It is expected
+ that @apply will be replaced with the ::part CSS pseudo-element.
+ After replacement copied lines can be removed.
+ */
+ @apply --layout-inline;
+ @apply --layout-center-center;
+ position: relative;
+ box-sizing: border-box;
+ min-width: 5.14em;
+ margin: 0 0.29em;
+ background: transparent;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ font: inherit;
+ text-transform: uppercase;
+ outline-width: 0;
+ border-top-left-radius: var(--border-radius);
+ border-top-right-radius: var(--border-radius);
+ border-bottom-right-radius: var(--border-radius);
+ border-bottom-left-radius: var(--border-radius);
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ padding: var(--spacing-m);
+
+ @apply --paper-font-common-base;
+ @apply --paper-button;
+ /* End of copy*/
+
+ /* paper-button sets this to anti-aliased, which appears different than
+ bold font elsewhere on macOS. */
+ -webkit-font-smoothing: initial;
+ align-items: center;
+ background-color: var(--background-color);
+ color: var(--text-color);
+ display: flex;
+ font-family: inherit;
+ justify-content: center;
+ margin: var(--margin, 0);
+ min-width: var(--border, 0);
+ padding: var(--padding, 4px 8px);
+ @apply --gr-button;
+ }
+ /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */
+ /* BEGIN: Copy from paper-button */
+ paper-button[elevation='1'] {
+ @apply --paper-material-elevation-1;
+ }
+ paper-button[elevation='2'] {
+ @apply --paper-material-elevation-2;
+ }
+ paper-button[elevation='3'] {
+ @apply --paper-material-elevation-3;
+ }
+ paper-button[elevation='4'] {
+ @apply --paper-material-elevation-4;
+ }
+ paper-button[elevation='5'] {
+ @apply --paper-material-elevation-5;
+ }
+ /* END: Copy from paper-button */
+ paper-button:hover {
+ background: linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
+ var(--background-color);
+ }
+
+ /* Some mobile browsers treat focused element as hovered element.
+ As a result, element remains hovered after click (has grey background in default theme).
+ Use @media (hover:none) to remove background if
+ user's primary input mechanism can't hover over elements.
+ See: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover
+
+ Note 1: not all browsers support this media query
+ (see https://caniuse.com/#feat=css-media-interaction).
+ If browser doesn't support it, then the whole content of @media .. is ignored.
+ This is why the default behavior is placed outside of @media.
+ */
+ @media (hover: none) {
+ paper-button:hover {
+ background: transparent;
+ }
+ }
+
+ :host([primary]) {
+ --background-color: var(--primary-button-background-color);
+ --text-color: var(--primary-button-text-color);
+ }
+ :host([link][primary]) {
+ --text-color: var(--primary-button-background-color);
+ }
+
+ /* Keep below color definition for primary so that this takes precedence
+ when disabled. */
+ :host([disabled]),
+ :host([loading]) {
+ --background-color: var(--disabled-button-background-color);
+ --text-color: var(--deemphasized-text-color);
+ cursor: default;
+ }
+
+ /* Styles for link buttons specifically */
+ :host([link]) {
+ --background-color: transparent;
+ --margin: 0;
+ --padding: var(--spacing-s);
+ }
+ :host([disabled][link]),
+ :host([loading][link]) {
+ --background-color: transparent;
+ --text-color: var(--deemphasized-text-color);
+ cursor: default;
+ }
+
+ /* Styles for the optional down arrow */
+ :host(:not([down-arrow])) .downArrow {
+ display: none;
+ }
+ :host([down-arrow]) .downArrow {
+ border-top: 0.36em solid #ccc;
+ border-left: 0.36em solid transparent;
+ border-right: 0.36em solid transparent;
+ margin-bottom: var(--spacing-xxs);
+ margin-left: var(--spacing-m);
+ transition: border-top-color 200ms;
+ }
+ :host([down-arrow]) paper-button:hover .downArrow {
+ border-top-color: var(--deemphasized-text-color);
+ }
+ `,
+ ];
}
- @property({
- computed: 'computeAriaDisabled(disabled, loading)',
- reflectToAttribute: true,
- type: String,
- })
- ariaDisabled!: string;
-
- computeAriaDisabled() {
- return this._disabled ? 'true' : 'false';
+ override render() {
+ return html`<paper-button
+ ?raised="${!this.link}"
+ ?disabled="${this.disabled || this.loading}"
+ role="button"
+ tabindex="-1"
+ part="paper-button"
+ class="${this.voteChip ? 'voteChip' : ''}"
+ >
+ ${this.loading ? html`<span class="loadingSpin"></span>` : ''}
+ <slot></slot>
+ <i class="downArrow"></i>
+ </paper-button>`;
}
- computePaperButtonClass(voteChip?: boolean) {
- return voteChip ? 'voteChip' : '';
- }
-
- private readonly reporting: ReportingService = appContext.reportingService;
-
constructor() {
super();
- this._initialTabindex = this.getAttribute('tabindex') || '0';
- // TODO(TS): try avoid using unknown
- this.addEventListener('click', e =>
- this._handleAction(e as unknown as PolymerEvent)
- );
+ this.initialTabindex = this.getAttribute('tabindex') || '0';
+ this.addEventListener('click', e => this._handleAction(e));
this.addEventListener('keydown', e =>
this._handleKeydown(e as unknown as CustomKeyboardEvent)
);
}
- override ready() {
- super.ready();
- this._ensureAttribute('role', 'button');
- this._ensureAttribute('tabindex', '0');
+ override updated(changedProperties: PropertyValues) {
+ if (changedProperties.has('disabled')) {
+ this.setAttribute(
+ 'tabindex',
+ this.disabled ? '-1' : this.initialTabindex || '0'
+ );
+ }
+ if (changedProperties.has('loading') || changedProperties.has('disabled')) {
+ this.setAttribute(
+ 'aria-disabled',
+ this.disabled || this.loading ? 'true' : 'false'
+ );
+ }
}
- _handleAction(e: PolymerEvent) {
- if (this._disabled) {
+ override connectedCallback() {
+ super.connectedCallback();
+ if (!this.getAttribute('role')) {
+ this.setAttribute('role', 'button');
+ }
+ if (!this.getAttribute('tabindex')) {
+ this.setAttribute('tabindex', '0');
+ }
+ }
+
+ _handleAction(e: MouseEvent) {
+ if (this.disabled || this.loading) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
@@ -127,15 +280,6 @@
this.reporting.reportInteraction('button-click', {path: getEventPath(e)});
}
- @observe('disabled')
- _disabledChanged(disabled: boolean) {
- this.setAttribute(
- 'tabindex',
- disabled ? '-1' : this._initialTabindex || '0'
- );
- this.updateStyles();
- }
-
_handleKeydown(e: CustomKeyboardEvent) {
if (isModifierPressed(e)) {
return;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts
deleted file mode 100644
index 22ec2f4..0000000
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_html.ts
+++ /dev/null
@@ -1,188 +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="gr-voting-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <style include="gr-spinner-styles">
- /* general styles for all buttons */
- :host {
- --background-color: var(
- --button-background-color,
- var(--default-button-background-color)
- );
- --text-color: var(--default-button-text-color);
- display: inline-block;
- position: relative;
- }
- :host([hidden]) {
- display: none;
- }
- :host([no-uppercase]) paper-button {
- text-transform: none;
- }
- paper-button {
- /* The next lines contains a copy of paper-button style.
- Without a copy, the @apply works incorrectly with Polymer 2.
- @apply is deprecated and is not recommended to use. It is expected
- that @apply will be replaced with the ::part CSS pseudo-element.
- After replacement copied lines can be removed.
- */
- @apply --layout-inline;
- @apply --layout-center-center;
- position: relative;
- box-sizing: border-box;
- min-width: 5.14em;
- margin: 0 0.29em;
- background: transparent;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- -webkit-tap-highlight-color: transparent;
- font: inherit;
- text-transform: uppercase;
- outline-width: 0;
- border-top-left-radius: var(--border-radius);
- border-top-right-radius: var(--border-radius);
- border-bottom-right-radius: var(--border-radius);
- border-bottom-left-radius: var(--border-radius);
- -moz-user-select: none;
- -ms-user-select: none;
- -webkit-user-select: none;
- user-select: none;
- cursor: pointer;
- z-index: 0;
- padding: var(--spacing-m);
-
- @apply --paper-font-common-base;
- @apply --paper-button;
- /* End of copy*/
-
- /* paper-button sets this to anti-aliased, which appears different than
- bold font elsewhere on macOS. */
- -webkit-font-smoothing: initial;
- align-items: center;
- background-color: var(--background-color);
- color: var(--text-color);
- display: flex;
- font-family: inherit;
- justify-content: center;
- margin: var(--margin, 0);
- min-width: var(--border, 0);
- padding: var(--padding, 4px 8px);
- @apply --gr-button;
- }
- /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */
- /* BEGIN: Copy from paper-button */
- paper-button[elevation='1'] {
- @apply --paper-material-elevation-1;
- }
- paper-button[elevation='2'] {
- @apply --paper-material-elevation-2;
- }
- paper-button[elevation='3'] {
- @apply --paper-material-elevation-3;
- }
- paper-button[elevation='4'] {
- @apply --paper-material-elevation-4;
- }
- paper-button[elevation='5'] {
- @apply --paper-material-elevation-5;
- }
- /* END: Copy from paper-button */
- paper-button:hover {
- background: linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
- var(--background-color);
- }
-
- /* Some mobile browsers treat focused element as hovered element.
- As a result, element remains hovered after click (has grey background in default theme).
- Use @media (hover:none) to remove background if
- user's primary input mechanism can't hover over elements.
- See: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover
-
- Note 1: not all browsers support this media query
- (see https://caniuse.com/#feat=css-media-interaction).
- If browser doesn't support it, then the whole content of @media .. is ignored.
- This is why the default behavior is placed outside of @media.
- */
- @media (hover: none) {
- paper-button:hover {
- background: transparent;
- }
- }
-
- :host([primary]) {
- --background-color: var(--primary-button-background-color);
- --text-color: var(--primary-button-text-color);
- }
- :host([link][primary]) {
- --text-color: var(--primary-button-background-color);
- }
-
- /* Keep below color definition for primary so that this takes precedence
- when disabled. */
- :host([disabled]),
- :host([loading]) {
- --background-color: var(--disabled-button-background-color);
- --text-color: var(--deemphasized-text-color);
- cursor: default;
- }
-
- /* Styles for link buttons specifically */
- :host([link]) {
- --background-color: transparent;
- --margin: 0;
- --padding: var(--spacing-s);
- }
- :host([disabled][link]),
- :host([loading][link]) {
- --background-color: transparent;
- --text-color: var(--deemphasized-text-color);
- cursor: default;
- }
-
- /* Styles for the optional down arrow */
- :host(:not([down-arrow])) .downArrow {
- display: none;
- }
- :host([down-arrow]) .downArrow {
- border-top: 0.36em solid #ccc;
- border-left: 0.36em solid transparent;
- border-right: 0.36em solid transparent;
- margin-bottom: var(--spacing-xxs);
- margin-left: var(--spacing-m);
- transition: border-top-color 200ms;
- }
- :host([down-arrow]) paper-button:hover .downArrow {
- border-top-color: var(--deemphasized-text-color);
- }
- </style>
- <paper-button
- raised="[[!link]]"
- disabled="[[_disabled]]"
- tabindex="-1"
- part="paper-button"
- class$="[[computePaperButtonClass(voteChip)]]"
- >
- <template is="dom-if" if="[[loading]]">
- <span class="loadingSpin"></span>
- </template>
- <slot></slot>
- <i class="downArrow"></i>
- </paper-button>
-`;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.ts b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.ts
index f0f122a..0149bd5 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.ts
@@ -17,6 +17,7 @@
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
import '../../../test/common-test-setup-karma';
+import './gr-button';
import {addListener} from '@polymer/polymer/lib/utils/gestures';
import {appContext} from '../../../services/app-context';
import {html} from '@polymer/polymer/lib/utils/html-tag';
@@ -49,23 +50,26 @@
return spy;
};
- setup(() => {
+ setup(async () => {
element = basicFixture.instantiate();
+ await element.updateComplete;
});
- test('disabled is set by disabled', () => {
+ test('disabled is set by disabled', async () => {
const paperBtn = queryAndAssert<PaperButtonElement>(
element,
'paper-button'
);
assert.isFalse(paperBtn.disabled);
element.disabled = true;
+ await element.updateComplete;
assert.isTrue(paperBtn.disabled);
element.disabled = false;
+ await element.updateComplete;
assert.isFalse(paperBtn.disabled);
});
- test('loading set from listener', () => {
+ test('loading set from listener', async () => {
let resolve: Function;
element.addEventListener('click', e => {
const target = e.target as HTMLElement;
@@ -78,36 +82,44 @@
);
assert.isFalse(paperBtn.disabled);
MockInteractions.tap(element);
+ await element.updateComplete;
assert.isTrue(paperBtn.disabled);
assert.isTrue(element.hasAttribute('loading'));
resolve!();
- flush();
+ await element.updateComplete;
assert.isFalse(paperBtn.disabled);
assert.isFalse(element.hasAttribute('loading'));
});
- test('tabindex should be -1 if disabled', () => {
+ test('tabindex should be -1 if disabled', async () => {
element.disabled = true;
- assert.isTrue(element.getAttribute('tabindex') === '-1');
+ await element.updateComplete;
+ assert.equal(element.getAttribute('tabindex'), '-1');
});
// Regression tests for Issue: 11969
- test('tabindex should be reset to 0 if enabled', () => {
+ test('tabindex should be reset to 0 if enabled', async () => {
element.disabled = false;
+ await element.updateComplete;
assert.equal(element.getAttribute('tabindex'), '0');
element.disabled = true;
+ await element.updateComplete;
assert.equal(element.getAttribute('tabindex'), '-1');
element.disabled = false;
+ await element.updateComplete;
assert.equal(element.getAttribute('tabindex'), '0');
});
- test('tabindex should be preserved', () => {
+ test('tabindex should be preserved', async () => {
const tabIndexElement = tabindexFixture.instantiate() as GrButton;
tabIndexElement.disabled = false;
+ await element.updateComplete;
assert.equal(tabIndexElement.getAttribute('tabindex'), '3');
tabIndexElement.disabled = true;
+ await element.updateComplete;
assert.equal(tabIndexElement.getAttribute('tabindex'), '-1');
tabIndexElement.disabled = false;
+ await element.updateComplete;
assert.equal(tabIndexElement.getAttribute('tabindex'), '3');
});
@@ -152,8 +164,9 @@
}
suite('disabled', () => {
- setup(() => {
+ setup(async () => {
element.disabled = true;
+ await element.updateComplete;
});
for (const eventName of ['tap', 'click']) {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 418cd0e..fa04860 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -20,7 +20,6 @@
import '../../plugins/gr-endpoint-param/gr-endpoint-param';
import '../gr-button/gr-button';
import '../gr-dialog/gr-dialog';
-import '../gr-date-formatter/gr-date-formatter';
import '../gr-formatted-text/gr-formatted-text';
import '../gr-icons/gr-icons';
import '../gr-overlay/gr-overlay';
@@ -675,7 +674,13 @@
@observe('comment.message')
_commentMessageChanged(message: string) {
- this._messageText = message || '';
+ /*
+ * Only overwrite the message text user has typed if there is no existing
+ * text typed by the user. This prevents the bug where creating another
+ * comment triggered a recomputation of comments and the text written by
+ * the user was lost.
+ */
+ if (!this._messageText) this._messageText = message || '';
}
_messageTextChanged(_: string, oldValue: string) {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
index b00bf8b..4cb7738 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_html.ts
@@ -313,7 +313,7 @@
<template is="dom-if" if="[[comment.updated]]">
<span class="date" tabindex="0" on-click="_handleAnchorClick">
<gr-date-formatter
- has-tooltip=""
+ withTooltip
date-str="[[comment.updated]]"
></gr-date-formatter>
</span>
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
index 31a1614..b963d4b 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.ts
@@ -217,6 +217,29 @@
assert.isTrue(storageStub.called);
});
+ test('comment message sets messageText only when empty', () => {
+ element.changeNum = 1 as NumericChangeId;
+ element.patchNum = 1 as PatchSetNum;
+ element._messageText = '';
+ element.comment = {
+ author: {
+ name: 'Mr. Peanutbutter',
+ email: 'tenn1sballchaser@aol.com' as EmailAddress,
+ },
+ line: 5,
+ path: 'test',
+ __editing: true,
+ __draft: true,
+ message: 'hello world',
+ };
+ // messageText was empty so overwrite the message now
+ assert.equal(element._messageText, 'hello world');
+
+ element.comment!.message = 'new message';
+ // messageText was already set so do not overwrite it
+ assert.equal(element._messageText, 'hello world');
+ });
+
test('_getPatchNum', () => {
element.side = 'PARENT';
element.patchNum = 1 as PatchSetNum;
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
index de2a017..99be86b 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.ts
@@ -123,17 +123,20 @@
part="text-container-style"
/>
</iron-input>
- <gr-button
- id="copy-clipboard-button"
- link=""
+ <gr-tooltip-content
?has-tooltip=${this.hasTooltip}
- class="copyToClipboard"
title="${ifDefined(this.buttonTitle)}"
- @click="${this._copyToClipboard}"
- aria-label="Click to copy to clipboard"
>
- <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
- </gr-button>
+ <gr-button
+ id="copy-clipboard-button"
+ link=""
+ class="copyToClipboard"
+ @click="${this._copyToClipboard}"
+ aria-label="Click to copy to clipboard"
+ >
+ <iron-icon id="icon" icon="gr-icons:content-copy"></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>
</div> `;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
index 437e7e8..99f9265 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.ts
@@ -14,11 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import '../../../styles/shared-styles';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-date-formatter_html';
-import {TooltipMixin} from '../../../mixins/gr-tooltip-mixin/gr-tooltip-mixin';
-import {property, customElement} from '@polymer/decorators';
+import '../gr-tooltip-content/gr-tooltip-content';
+import {css, html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators';
import {
parseDate,
fromNow,
@@ -75,16 +73,9 @@
}
}
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = TooltipMixin(PolymerElement);
-
@customElement('gr-date-formatter')
-export class GrDateFormatter extends base {
- static get template() {
- return htmlTemplate;
- }
-
- @property({type: String, notify: true})
+export class GrDateFormatter extends LitElement {
+ @property({type: String})
dateStr: string | undefined = undefined;
@property({type: Boolean})
@@ -95,30 +86,20 @@
* native browser tooltip.
*/
@property({type: Boolean})
- override hasTooltip = false;
+ withTooltip = false;
@property({type: Boolean})
showYesterday = false;
- /**
- * The title to be used as the native tooltip or by the tooltip behavior.
- */
- @property({
- type: String,
- reflectToAttribute: true,
- computed: '_computeFullDateStr(dateStr, _timeFormat, _dateFormat)',
- })
- override title = '';
-
/** @type {?{short: string, full: string}} */
@property({type: Object})
- _dateFormat?: DateFormatPair;
+ private dateFormat?: DateFormatPair;
@property({type: String})
- _timeFormat?: string;
+ private timeFormat?: string;
@property({type: Boolean})
- _relative = false;
+ private relative = false;
@property({type: Boolean})
forceRelative = false;
@@ -132,76 +113,110 @@
super();
}
+ static override get styles() {
+ return [
+ css`
+ host {
+ color: inherit;
+ display: inline;
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ if (!this.withTooltip) {
+ return this.renderDateString();
+ }
+
+ const fullDateStr = this.computeFullDateStr();
+ if (!fullDateStr) {
+ return this.renderDateString();
+ }
+ return html`
+ <gr-tooltip-content has-tooltip title=${fullDateStr}>
+ ${this.renderDateString()}
+ </gr-tooltip-content>
+ `;
+ }
+
+ private renderDateString() {
+ return html` <span>${this._computeDateStr()}</span>`;
+ }
+
override connectedCallback() {
super.connectedCallback();
this._loadPreferences();
}
+ // private but used by tests
_getUtcOffsetString() {
return utcOffsetString();
}
+ // private but used by tests
_loadPreferences() {
return this._getLoggedIn().then(loggedIn => {
if (!loggedIn) {
- this._timeFormat = TimeFormats.TIME_24;
- this._dateFormat = DateFormats.STD;
- this._relative = this.forceRelative;
+ this.timeFormat = TimeFormats.TIME_24;
+ this.dateFormat = DateFormats.STD;
+ this.relative = this.forceRelative;
return;
}
- return Promise.all([this._loadTimeFormat(), this._loadRelative()]);
+ return Promise.all([this._loadTimeFormat(), this.loadRelative()]);
});
}
+ // private but used in gr/file-list_test.js
_loadTimeFormat() {
- return this._getPreferences().then(preferences => {
+ return this.getPreferences().then(preferences => {
if (!preferences) {
throw Error('Preferences is not set');
}
- this._decideTimeFormat(preferences.time_format);
- this._decideDateFormat(preferences.date_format);
+ this.decideTimeFormat(preferences.time_format);
+ this.decideDateFormat(preferences.date_format);
});
}
- _decideTimeFormat(timeFormat: TimeFormat) {
+ private decideTimeFormat(timeFormat: TimeFormat) {
switch (timeFormat) {
case TimeFormat.HHMM_12:
- this._timeFormat = TimeFormats.TIME_12;
+ this.timeFormat = TimeFormats.TIME_12;
break;
case TimeFormat.HHMM_24:
- this._timeFormat = TimeFormats.TIME_24;
+ this.timeFormat = TimeFormats.TIME_24;
break;
default:
assertNever(timeFormat, `Invalid time format: ${timeFormat}`);
}
}
- _decideDateFormat(dateFormat: DateFormat) {
+ private decideDateFormat(dateFormat: DateFormat) {
switch (dateFormat) {
case DateFormat.STD:
- this._dateFormat = DateFormats.STD;
+ this.dateFormat = DateFormats.STD;
break;
case DateFormat.US:
- this._dateFormat = DateFormats.US;
+ this.dateFormat = DateFormats.US;
break;
case DateFormat.ISO:
- this._dateFormat = DateFormats.ISO;
+ this.dateFormat = DateFormats.ISO;
break;
case DateFormat.EURO:
- this._dateFormat = DateFormats.EURO;
+ this.dateFormat = DateFormats.EURO;
break;
case DateFormat.UK:
- this._dateFormat = DateFormats.UK;
+ this.dateFormat = DateFormats.UK;
break;
default:
assertNever(dateFormat, `Invalid date format: ${dateFormat}`);
}
}
- _loadRelative() {
- return this._getPreferences().then(prefs => {
+ private loadRelative() {
+ return this.getPreferences().then(prefs => {
// prefs.relative_date_in_change_table is not set when false.
- this._relative =
+ this.relative =
this.forceRelative || !!(prefs && prefs.relative_date_in_change_table);
});
}
@@ -210,70 +225,60 @@
return this.restApiService.getLoggedIn();
}
- _getPreferences() {
+ private getPreferences() {
return this.restApiService.getPreferences();
}
- _computeDateStr(
- dateStr?: Timestamp,
- timeFormat?: string,
- dateFormat?: DateFormatPair,
- relative?: boolean,
- showDateAndTime?: boolean,
- showYesterday?: boolean
- ) {
- if (!dateStr || !timeFormat || !dateFormat) {
+ // private but used by tests
+ _computeDateStr() {
+ if (!this.dateStr || !this.timeFormat || !this.dateFormat) {
return '';
}
- const date = parseDate(dateStr);
+ const date = parseDate(this.dateStr as Timestamp);
if (!isValidDate(date)) {
return '';
}
- if (relative) {
+ if (this.relative) {
return fromNow(date, this.relativeOptionNoAgo);
}
const now = new Date();
- let format = dateFormat.full;
+ let format = this.dateFormat.full;
if (isWithinDay(now, date)) {
- format = timeFormat;
- } else if (showYesterday && wasYesterday(now, date)) {
- return `Yesterday at ${formatDate(date, timeFormat)}`;
+ format = this.timeFormat;
+ } else if (this.showYesterday && wasYesterday(now, date)) {
+ return `Yesterday at ${formatDate(date, this.timeFormat)}`;
} else {
if (isWithinHalfYear(now, date)) {
- format = dateFormat.short;
+ format = this.dateFormat.short;
}
- if (this.showDateAndTime || showDateAndTime) {
- format = `${format} ${timeFormat}`;
+ if (this.showDateAndTime || this.showDateAndTime) {
+ format = `${format} ${this.timeFormat}`;
}
}
return formatDate(date, format);
}
- _timeToSecondsFormat(timeFormat: string | undefined) {
- return timeFormat === TimeFormats.TIME_12
- ? TimeFormats.TIME_12_WITH_SEC
- : TimeFormats.TIME_24_WITH_SEC;
- }
-
- _computeFullDateStr(
- dateStr?: Timestamp,
- timeFormat?: string,
- dateFormat?: DateFormatPair
- ) {
+ private computeFullDateStr() {
// Polymer 2: check for undefined
- if ([dateStr, timeFormat].includes(undefined) || !dateFormat) {
+ if (
+ [this.dateStr, this.timeFormat].includes(undefined) ||
+ !this.dateFormat
+ ) {
return undefined;
}
- if (!dateStr) {
+ if (!this.dateStr) {
return '';
}
- const date = parseDate(dateStr);
+ const date = parseDate(this.dateStr as Timestamp);
if (!isValidDate(date)) {
return '';
}
- let format = dateFormat.full + ', ';
- format += this._timeToSecondsFormat(timeFormat);
+ let format = this.dateFormat.full + ', ';
+ format +=
+ this.timeFormat === TimeFormats.TIME_12
+ ? TimeFormats.TIME_12_WITH_SEC
+ : TimeFormats.TIME_24_WITH_SEC;
return formatDate(date, format) + this._getUtcOffsetString();
}
}
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts
deleted file mode 100644
index 4808832..0000000
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_html.ts
+++ /dev/null
@@ -1,30 +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>
- :host {
- color: inherit;
- display: inline;
- }
- </style>
- <span>
- [[_computeDateStr(dateStr, _timeFormat, _dateFormat, _relative,
- showDateAndTime, showYesterday)]]
- </span>
-`;
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
index 9a96c2d..860a7e7 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.js
@@ -22,14 +22,18 @@
import {stubRestApi} from '../../../test/test-utils.js';
const basicFixture = fixtureFromTemplate(html`
-<gr-date-formatter date-str="2015-09-24 23:30:17.033000000"></gr-date-formatter>
+<gr-date-formatter withTooltip dateStr="2015-09-24 23:30:17.033000000">
+</gr-date-formatter>
+`);
+
+const lightFixture = fixtureFromTemplate(html`
+<gr-date-formatter dateStr="2015-09-24 23:30:17.033000000"></gr-date-formatter>
`);
suite('gr-date-formatter tests', () => {
let element;
setup(() => {
-
});
/**
@@ -41,7 +45,7 @@
return d;
}
- function testDates(nowStr, dateStr, expected, expectedWithDateAndTime,
+ async function testDates(nowStr, dateStr, expected, expectedWithDateAndTime,
expectedTooltip) {
// Normalize and convert the date to mimic server response.
dateStr = normalizedDate(dateStr)
@@ -50,13 +54,13 @@
.slice(0, -1);
sinon.useFakeTimers(normalizedDate(nowStr).getTime());
element.dateStr = dateStr;
- flush();
- const span = element.shadowRoot
- .querySelector('span');
+ await element.updateComplete;
+ const span = element.shadowRoot.querySelector('span');
+ const tooltip = element.shadowRoot.querySelector('gr-tooltip-content');
assert.equal(span.textContent.trim(), expected);
- assert.equal(element.title, expectedTooltip);
+ assert.equal(tooltip.title, expectedTooltip);
element.showDateAndTime = true;
- flush();
+ await element.updateComplete;
assert.equal(span.textContent.trim(), expectedWithDateAndTime);
}
@@ -81,35 +85,37 @@
test('invalid dates are quietly rejected', () => {
assert.notOk((new Date('foo')).valueOf());
- assert.equal(element._computeDateStr('foo', 'h:mm A'), '');
+ element.dateStr = 'foo';
+ element.timeFormat = 'h:mm A';
+ assert.equal(element._computeDateStr(), '');
});
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
'Jul 29, 2015, 15:34:14');
});
- test('Within 24 hours on different days', () => {
- testDates('2015-07-29 03:34:14.985000000',
+ test('Within 24 hours on different days', async () => {
+ await testDates('2015-07-29 03:34:14.985000000',
'2015-07-28 20:25:14.985000000',
'Jul 28',
'Jul 28 20:25',
'Jul 28, 2015, 20:25:14');
});
- test('More than 24 hours but less than six months', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('More than 24 hours but less than six months', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-06-15 03:25:14.985000000',
'Jun 15',
'Jun 15 03:25',
'Jun 15, 2015, 03:25:14');
});
- test('More than six months', () => {
- testDates('2015-09-15 20:34:00.000000000',
+ test('More than six months', async () => {
+ await testDates('2015-09-15 20:34:00.000000000',
'2015-01-15 03:25:00.000000000',
'Jan 15, 2015',
'Jan 15, 2015 03:25',
@@ -128,24 +134,24 @@
return element._loadPreferences();
}));
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
'07/29/15, 15:34:14');
});
- test('Within 24 hours on different days', () => {
- testDates('2015-07-29 03:34:14.985000000',
+ test('Within 24 hours on different days', async () => {
+ await testDates('2015-07-29 03:34:14.985000000',
'2015-07-28 20:25:14.985000000',
'07/28',
'07/28 20:25',
'07/28/15, 20:25:14');
});
- test('More than 24 hours but less than six months', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('More than 24 hours but less than six months', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-06-15 03:25:14.985000000',
'06/15',
'06/15 03:25',
@@ -164,24 +170,24 @@
return element._loadPreferences();
}));
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
'2015-07-29, 15:34:14');
});
- test('Within 24 hours on different days', () => {
- testDates('2015-07-29 03:34:14.985000000',
+ test('Within 24 hours on different days', async () => {
+ await testDates('2015-07-29 03:34:14.985000000',
'2015-07-28 20:25:14.985000000',
'07-28',
'07-28 20:25',
'2015-07-28, 20:25:14');
});
- test('More than 24 hours but less than six months', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('More than 24 hours but less than six months', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-06-15 03:25:14.985000000',
'06-15',
'06-15 03:25',
@@ -200,24 +206,24 @@
return element._loadPreferences();
}));
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
'29.07.2015, 15:34:14');
});
- test('Within 24 hours on different days', () => {
- testDates('2015-07-29 03:34:14.985000000',
+ test('Within 24 hours on different days', async () => {
+ await testDates('2015-07-29 03:34:14.985000000',
'2015-07-28 20:25:14.985000000',
'28. Jul',
'28. Jul 20:25',
'28.07.2015, 20:25:14');
});
- test('More than 24 hours but less than six months', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('More than 24 hours but less than six months', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-06-15 03:25:14.985000000',
'15. Jun',
'15. Jun 03:25',
@@ -236,24 +242,24 @@
return element._loadPreferences();
}));
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'15:34',
'15:34',
'29/07/2015, 15:34:14');
});
- test('Within 24 hours on different days', () => {
- testDates('2015-07-29 03:34:14.985000000',
+ test('Within 24 hours on different days', async () => {
+ await testDates('2015-07-29 03:34:14.985000000',
'2015-07-28 20:25:14.985000000',
'28/07',
'28/07 20:25',
'28/07/2015, 20:25:14');
});
- test('More than 24 hours but less than six months', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('More than 24 hours but less than six months', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-06-15 03:25:14.985000000',
'15/06',
'15/06 03:25',
@@ -273,8 +279,8 @@
})
);
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
@@ -294,8 +300,8 @@
})
);
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
@@ -315,8 +321,8 @@
})
);
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
@@ -336,8 +342,8 @@
})
);
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
@@ -357,8 +363,8 @@
})
);
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'3:34 PM',
'3:34 PM',
@@ -377,16 +383,16 @@
return element._loadPreferences();
}));
- test('Within 24 hours on same day', () => {
- testDates('2015-07-29 20:34:14.985000000',
+ test('Within 24 hours on same day', async () => {
+ await testDates('2015-07-29 20:34:14.985000000',
'2015-07-29 15:34:14.985000000',
'5 hours ago',
'5 hours ago',
'Jul 29, 2015, 3:34:14 PM');
});
- test('More than six months', () => {
- testDates('2015-09-15 20:34:00.000000000',
+ test('More than six months', async () => {
+ await testDates('2015-09-15 20:34:00.000000000',
'2015-01-15 03:25:00.000000000',
'8 months ago',
'8 months ago',
@@ -405,10 +411,10 @@
}));
test('Preferences are respected', () => {
- assert.equal(element._timeFormat, 'h:mm A');
- assert.equal(element._dateFormat.short, 'MM/DD');
- assert.equal(element._dateFormat.full, 'MM/DD/YY');
- assert.isTrue(element._relative);
+ assert.equal(element.timeFormat, 'h:mm A');
+ assert.equal(element.dateFormat.short, 'MM/DD');
+ assert.equal(element.dateFormat.full, 'MM/DD/YY');
+ assert.isTrue(element.relative);
});
});
@@ -419,10 +425,38 @@
}));
test('Default preferences are respected', () => {
- assert.equal(element._timeFormat, 'HH:mm');
- assert.equal(element._dateFormat.short, 'MMM DD');
- assert.equal(element._dateFormat.full, 'MMM DD, YYYY');
- assert.isFalse(element._relative);
+ assert.equal(element.timeFormat, 'HH:mm');
+ assert.equal(element.dateFormat.short, 'MMM DD');
+ assert.equal(element.dateFormat.full, 'MMM DD, YYYY');
+ assert.isFalse(element.relative);
+ });
+ });
+
+ suite('with tooltip', () => {
+ setup(async () => {
+ await stubRestAPI(null);
+ element = basicFixture.instantiate();
+ await element._loadPreferences();
+ await element.updateComplete;
+ });
+
+ test('Tooltip is present', () => {
+ const tooltip = element.shadowRoot.querySelector('gr-tooltip-content');
+ assert.isOk(tooltip);
+ });
+ });
+
+ suite('without tooltip', () => {
+ setup(async () => {
+ await stubRestAPI(null);
+ element = lightFixture.instantiate();
+ await element._loadPreferences();
+ await element.updateComplete;
+ });
+
+ test('Tooltip is absent', () => {
+ const tooltip = element.shadowRoot.querySelector('gr-tooltip-content');
+ assert.isNotOk(tooltip);
});
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
index 18a46a0..9ec1d39 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
@@ -123,6 +123,7 @@
class="dropdown-trigger"
on-click="_showDropdownTapHandler"
slot="dropdown-trigger"
+ no-uppercase
>
<span id="triggerText">[[text]]</span>
<gr-copy-clipboard
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
index f1f6bf8..076553b 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_html.ts
@@ -128,7 +128,7 @@
<span class="value">[[_computeReason(change)]]</span>
<template is="dom-if" if="[[_computeLastUpdate(change)]]">
(<gr-date-formatter
- has-tooltip
+ withTooltip
date-str="[[_computeLastUpdate(change)]]"
></gr-date-formatter
>)
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
index e9a224c..82f64d0 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account_test.js
@@ -109,7 +109,7 @@
stubRestApi('removeChangeReviewer').returns(Promise.resolve({ok: true}));
const reloadListener = sinon.spy();
element._target.addEventListener('reload', reloadListener);
- flush();
+ await flush();
const button = element.shadowRoot.querySelector('.removeReviewerOrCC');
assert.isOk(button);
assert.equal(button.innerText, 'Remove Reviewer');
@@ -132,7 +132,7 @@
const reloadListener = sinon.spy();
element._target.addEventListener('reload', reloadListener);
- flush();
+ await flush();
const button = element.shadowRoot.querySelector('.changeReviewerOrCC');
assert.isOk(button);
@@ -156,7 +156,7 @@
stubRestApi('removeChangeReviewer').returns(Promise.resolve({ok: true}));
const reloadListener = sinon.spy();
element._target.addEventListener('reload', reloadListener);
- flush();
+ await flush();
const button = element.shadowRoot.querySelector('.changeReviewerOrCC');
assert.isOk(button);
@@ -180,7 +180,7 @@
const reloadListener = sinon.spy();
element._target.addEventListener('reload', reloadListener);
- flush();
+ await flush();
const button = element.shadowRoot.querySelector('.removeReviewerOrCC');
assert.equal(button.innerText, 'Remove CC');
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.js
index 203784d..87f6052 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.js
@@ -125,7 +125,7 @@
.querySelector('[data-action-key="' + key + '"]'));
});
- test('action button properties', () => {
+ test('action button properties', async () => {
const key = changeActions.add(changeActions.ActionType.REVISION, 'Bork!');
flush();
const button = element.shadowRoot
@@ -137,17 +137,17 @@
changeActions.setTitle(key, 'Yo hint');
changeActions.setEnabled(key, false);
changeActions.setIcon(key, 'pupper');
- flush();
+ await flush();
assert.equal(button.getAttribute('data-label'), 'Yo');
- assert.equal(button.getAttribute('title'), 'Yo hint');
+ assert.equal(button.parentElement.getAttribute('title'), 'Yo hint');
assert.isTrue(button.disabled);
assert.equal(button.querySelector('iron-icon').icon,
'gr-icons:pupper');
});
- test('hide action buttons', () => {
+ test('hide action buttons', async () => {
const key = changeActions.add(changeActions.ActionType.REVISION, 'Bork!');
- flush();
+ await flush();
let button = element.shadowRoot
.querySelector('[data-action-key="' + key + '"]');
assert.isOk(button);
@@ -168,7 +168,7 @@
.querySelector('[data-action-key="' + key + '"]'));
changeActions.setActionOverflow(
changeActions.ActionType.REVISION, key, true);
- flush();
+ await flush();
assert.isNotOk(element.shadowRoot
.querySelector('[data-action-key="' + key + '"]'));
assert.isFalse(element.$.moreActions.hidden);
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
index f31b57f..723f8c1 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info_html.ts
@@ -116,16 +116,17 @@
></gr-account-link>
</td>
<td>
- <gr-button
- link=""
- aria-label="Remove vote"
- on-click="_onDeleteVote"
- tooltip="Remove vote"
- data-account-id$="[[mappedLabel.account._account_id]]"
- class$="deleteBtn [[_computeDeleteClass(mappedLabel.account, mutable, change)]]"
- >
- <iron-icon icon="gr-icons:delete"></iron-icon>
- </gr-button>
+ <gr-tooltip-content has-tooltip="" title="Remove vote">
+ <gr-button
+ link=""
+ aria-label="Remove vote"
+ on-click="_onDeleteVote"
+ data-account-id$="[[mappedLabel.account._account_id]]"
+ class$="deleteBtn [[_computeDeleteClass(mappedLabel.account, mutable, change)]]"
+ >
+ <iron-icon icon="gr-icons:delete"></iron-icon>
+ </gr-button>
+ </gr-tooltip-content>
</td>
</tr>
</template>
diff --git a/polygerrit-ui/app/styles/gr-voting-styles.ts b/polygerrit-ui/app/styles/gr-voting-styles.ts
index 12d0784..a623d99 100644
--- a/polygerrit-ui/app/styles/gr-voting-styles.ts
+++ b/polygerrit-ui/app/styles/gr-voting-styles.ts
@@ -18,24 +18,26 @@
// Mark the file as a module. Otherwise typescript assumes this is a script
// and $_documentContainer is a global variable.
// See: https://www.typescriptlang.org/docs/handbook/modules.html
-export {};
+import {css} from 'lit';
+
+export const votingStyles = css`
+ .voteChip {
+ border: 1px solid var(--border-color);
+ /* max rounded */
+ border-radius: 1em;
+ box-shadow: none;
+ box-sizing: border-box;
+ min-width: 3em;
+ color: var(--vote-text-color);
+ }
+`;
const $_documentContainer = document.createElement('template');
-
$_documentContainer.innerHTML = `<dom-module id="gr-voting-styles">
<template>
<style>
- .voteChip {
- border: 1px solid var(--border-color);
- /* max rounded */
- border-radius: 1em;
- box-shadow: none;
- box-sizing: border-box;
- min-width: 3em;
- color: var(--vote-text-color);
- }
+ ${votingStyles.cssText}
</style>
</template>
</dom-module>`;
-
document.head.appendChild($_documentContainer.content);
diff --git a/polygerrit-ui/app/utils/common-util.ts b/polygerrit-ui/app/utils/common-util.ts
index 5370cf9..0002254 100644
--- a/polygerrit-ui/app/utils/common-util.ts
+++ b/polygerrit-ui/app/utils/common-util.ts
@@ -100,7 +100,7 @@
}
export function query<E extends Element = Element>(
- el: Element | undefined,
+ el: Element | null | undefined,
selector: string
): E | undefined {
if (!el) return undefined;
@@ -109,7 +109,7 @@
}
export function queryAndAssert<E extends Element = Element>(
- el: Element | undefined,
+ el: Element | null | undefined,
selector: string
): E {
const found = query<E>(el, selector);
diff --git a/polygerrit-ui/app/utils/dom-util.ts b/polygerrit-ui/app/utils/dom-util.ts
index 16129af..7b1f3e3 100644
--- a/polygerrit-ui/app/utils/dom-util.ts
+++ b/polygerrit-ui/app/utils/dom-util.ts
@@ -171,7 +171,7 @@
* getEventPath(e); // eg: div.class1>p#pid.class2
* }
*/
-export function getEventPath<T extends PolymerEvent>(e?: T) {
+export function getEventPath<T extends MouseEvent>(e?: T) {
if (!e) return '';
let path = e.composedPath();