Migrate gr-account-entry to lit
Release-Notes: skip
Change-Id: I97e3fd33acf0936134a7fa33b51c22226fd2ba6e
(cherry picked from commit 51f56467069d1501f41c9d59fda2e934674c308a)
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.ts
index 2972d24..13cead6 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.ts
@@ -103,6 +103,7 @@
element.$.ccs.entry!.setText('test');
MockInteractions.tap(queryAndAssert(element, 'gr-button.send'));
+ assert.isFalse(element.$.ccs.submitEntryText());
assert.isFalse(sendStub.called);
flush();
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 883fbfa..5ea5cb4 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
@@ -69,6 +69,7 @@
import {GrLabelScoreRow} from '../gr-label-score-row/gr-label-score-row';
import {GrLabelScores} from '../gr-label-scores/gr-label-scores';
import {GrThreadList} from '../gr-thread-list/gr-thread-list';
+import {GrAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
const basicFixture = fixtureFromElement('gr-reply-dialog');
@@ -1182,10 +1183,10 @@
);
// We should be focused on account entry input.
+ const reviewersEntry = queryAndAssert<GrAccountList>(element, '#reviewers');
assert.isTrue(
isFocusInsideElement(
- queryAndAssert<GrAccountList>(element, '#reviewers').entry!.$.input.$
- .input
+ queryAndAssert<GrAutocomplete>(reviewersEntry.entry, '#input').$.input
)
);
@@ -1243,16 +1244,20 @@
// We should be focused on account entry input.
if (cc) {
+ const ccsEntry = queryAndAssert<GrAccountList>(element, '#ccs');
assert.isTrue(
isFocusInsideElement(
- queryAndAssert<GrAccountList>(element, '#ccs').entry!.$.input.$.input
+ queryAndAssert<GrAutocomplete>(ccsEntry.entry, '#input').$.input
)
);
} else {
+ const reviewersEntry = queryAndAssert<GrAccountList>(
+ element,
+ '#reviewers'
+ );
assert.isTrue(
isFocusInsideElement(
- queryAndAssert<GrAccountList>(element, '#reviewers').entry!.$.input.$
- .input
+ queryAndAssert<GrAutocomplete>(reviewersEntry.entry, '#input').$.input
)
);
}
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.ts b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.ts
index acb8348..71023a1 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry.ts
@@ -14,30 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import '../../../styles/shared-styles';
+
import '../gr-autocomplete/gr-autocomplete';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-account-entry_html';
-import {customElement, property} from '@polymer/decorators';
import {
AutocompleteQuery,
GrAutocomplete,
} from '../gr-autocomplete/gr-autocomplete';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {LitElement, PropertyValues, html, css} from 'lit';
+import {customElement, property, query, state} from 'lit/decorators';
+import {BindValueChangeEvent} from '../../../types/events';
-export interface GrAccountEntry {
- $: {
- input: GrAutocomplete;
- };
-}
/**
* gr-account-entry is an element for entering account
* and/or group with autocomplete support.
*/
@customElement('gr-account-entry')
-export class GrAccountEntry extends PolymerElement {
- static get template() {
- return htmlTemplate;
- }
+export class GrAccountEntry extends LitElement {
+ @query('#input') private input?: GrAutocomplete;
/**
* Fired when an account is entered.
@@ -62,33 +56,70 @@
@property({type: String})
placeholder = '';
- @property({type: Object, notify: true})
+ @property({type: Object})
querySuggestions: AutocompleteQuery = () => Promise.resolve([]);
- @property({type: String, observer: '_inputTextChanged'})
- _inputText = '';
+ @state() private inputText = '';
+
+ static override get styles() {
+ return [
+ sharedStyles,
+ css`
+ gr-autocomplete {
+ display: inline-block;
+ flex: 1;
+ overflow: hidden;
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html`
+ <gr-autocomplete
+ id="input"
+ .borderless=${this.borderless}
+ .placeholder=${this.placeholder}
+ .query=${this.querySuggestions}
+ .allowNonSuggestedValues=${this.allowAnyInput}
+ @commit=${this.handleInputCommit}
+ clear-on-commit=""
+ warn-uncommitted=""
+ .text=${this.inputText}
+ .verticalOffset=${24}
+ @text-changed=${this.handleTextChanged}
+ >
+ </gr-autocomplete>
+ `;
+ }
+
+ override willUpdate(changedProperties: PropertyValues) {
+ if (changedProperties.has('inputText')) {
+ this.inputTextChanged();
+ }
+ }
get focusStart() {
- return this.$.input.focusStart;
+ return this.input!.focusStart;
}
override focus() {
- this.$.input.focus();
+ this.input!.focus();
}
clear() {
- this.$.input.clear();
+ this.input!.clear();
}
setText(text: string) {
- this.$.input.setText(text);
+ this.input!.setText(text);
}
getText() {
- return this.$.input.text;
+ return this.input!.text;
}
- _handleInputCommit(e: CustomEvent) {
+ private handleInputCommit(e: CustomEvent) {
this.dispatchEvent(
new CustomEvent('add', {
detail: {value: e.detail.value},
@@ -96,16 +127,20 @@
bubbles: true,
})
);
- this.$.input.focus();
+ this.input!.focus();
}
- _inputTextChanged(text: string) {
- if (text.length && this.allowAnyInput) {
+ private inputTextChanged() {
+ if (this.inputText.length && this.allowAnyInput) {
this.dispatchEvent(
new CustomEvent('account-text-changed', {bubbles: true, composed: true})
);
}
}
+
+ private handleTextChanged(e: BindValueChangeEvent) {
+ this.inputText = e.detail.value;
+ }
}
declare global {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts
deleted file mode 100644
index d84ef62..0000000
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_html.ts
+++ /dev/null
@@ -1,40 +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="shared-styles">
- gr-autocomplete {
- display: inline-block;
- flex: 1;
- overflow: hidden;
- }
- </style>
- <gr-autocomplete
- id="input"
- borderless="[[borderless]]"
- placeholder="[[placeholder]]"
- query="[[querySuggestions]]"
- allow-non-suggested-values="[[allowAnyInput]]"
- on-commit="_handleInputCommit"
- clear-on-commit=""
- warn-uncommitted=""
- text="{{_inputText}}"
- vertical-offset="24"
- >
- </gr-autocomplete>
-`;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.ts b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.ts
index 4bb2232..8b3452e 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-entry/gr-account-entry_test.ts
@@ -18,48 +18,58 @@
import '../../../test/common-test-setup-karma';
import './gr-account-entry';
import {GrAccountEntry} from './gr-account-entry';
-
-const basicFixture = fixtureFromElement('gr-account-entry');
+import {fixture, html} from '@open-wc/testing-helpers';
+import {queryAndAssert} from '../../../test/test-utils';
+import {GrAutocomplete} from '../gr-autocomplete/gr-autocomplete';
+import {PaperInputElementExt} from '../../../types/types';
suite('gr-account-entry tests', () => {
let element: GrAccountEntry;
- setup(() => {
- element = basicFixture.instantiate();
+ setup(async () => {
+ element = await fixture<GrAccountEntry>(html`
+ <gr-account-entry></gr-account-entry>
+ `);
});
- test('account-text-changed fired when input text changed and allowAnyInput', () => {
+ test('account-text-changed fired when input text changed and allowAnyInput', async () => {
// Spy on query, as that is called when _updateSuggestions proceeds.
const changeStub = sinon.stub();
element.allowAnyInput = true;
element.querySuggestions = () => Promise.resolve([]);
element.addEventListener('account-text-changed', changeStub);
- element.$.input.text = 'a';
+ queryAndAssert<GrAutocomplete>(element, '#input').text = 'a';
+ await element.updateComplete;
assert.isTrue(changeStub.calledOnce);
- element.$.input.text = 'ab';
+ queryAndAssert<GrAutocomplete>(element, '#input').text = 'ab';
+ await element.updateComplete;
assert.isTrue(changeStub.calledTwice);
});
- test(
- 'account-text-changed not fired when input text changed without ' +
- 'allowAnyInput',
- () => {
- // Spy on query, as that is called when _updateSuggestions proceeds.
- const changeStub = sinon.stub();
- element.querySuggestions = () => Promise.resolve([]);
- element.addEventListener('account-text-changed', changeStub);
- element.$.input.text = 'a';
- assert.isFalse(changeStub.called);
- }
- );
-
- test('setText', () => {
+ test('account-text-changed not fired when input text changed without allowAnyInput', async () => {
// Spy on query, as that is called when _updateSuggestions proceeds.
- const suggestSpy = sinon.spy(element.$.input, 'query');
- element.setText('test text');
- flush();
+ const changeStub = sinon.stub();
+ element.querySuggestions = () => Promise.resolve([]);
+ element.addEventListener('account-text-changed', changeStub);
+ queryAndAssert<GrAutocomplete>(element, '#input').text = 'a';
+ await element.updateComplete;
+ assert.isFalse(changeStub.called);
+ });
- assert.equal(element.$.input.$.input.value, 'test text');
+ test('setText', async () => {
+ // Spy on query, as that is called when _updateSuggestions proceeds.
+ const suggestSpy = sinon.spy(
+ queryAndAssert<GrAutocomplete>(element, '#input'),
+ 'query'
+ );
+ element.setText('test text');
+ await element.updateComplete;
+
+ const input = queryAndAssert<GrAutocomplete>(element, '#input');
+ assert.equal(
+ queryAndAssert<PaperInputElementExt>(input, '#input').value,
+ 'test text'
+ );
assert.isFalse(suggestSpy.called);
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
index cf24bcd..ddf177a 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.ts
@@ -39,8 +39,10 @@
import {
AutocompleteQuery,
AutocompleteSuggestion,
+ GrAutocomplete,
} from '../gr-autocomplete/gr-autocomplete';
import {ValueChangedEvent} from '../../../types/events';
+import {queryAndAssert} from '../../../utils/common-util';
const VALID_EMAIL_ALERT = 'Please input a valid email.';
@@ -404,7 +406,8 @@
private handleInputKeydown(e: KeyboardEvent) {
const target = e.target as GrAccountEntry;
- const input = this.getOwnNativeInput(target.$.input.$.input);
+ const entryInput = queryAndAssert<GrAutocomplete>(target, '#input');
+ const input = this.getOwnNativeInput(entryInput.$.input);
if (
input.selectionStart !== input.selectionEnd ||
input.selectionStart !== 0
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
index 7509023..5d7d312 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.ts
@@ -30,9 +30,11 @@
GroupName,
Suggestion,
} from '../../../types/common';
-import {queryAll} from '../../../test/test-utils';
+import {queryAll, queryAndAssert} from '../../../test/test-utils';
import {ReviewerSuggestionsProvider} from '../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
+import {GrAutocomplete} from '../gr-autocomplete/gr-autocomplete';
+import {GrAccountEntry} from '../gr-account-entry/gr-account-entry';
const basicFixture = fixtureFromElement('gr-account-list');
@@ -117,10 +119,14 @@
test('account entry only appears when editable', async () => {
element.readonly = false;
await element.updateComplete;
- assert.isFalse(element.entry!.hasAttribute('hidden'));
+ assert.isFalse(
+ queryAndAssert<GrAccountEntry>(element, '#entry').hasAttribute('hidden')
+ );
element.readonly = true;
await element.updateComplete;
- assert.isTrue(element.entry!.hasAttribute('hidden'));
+ assert.isTrue(
+ queryAndAssert<GrAccountEntry>(element, '#entry').hasAttribute('hidden')
+ );
});
test('addition and removal of account/group chips', async () => {
@@ -295,13 +301,19 @@
element.allowAnyInput = true;
await element.updateComplete;
- const getTextStub = sinon.stub(element.entry!, 'getText');
+ const getTextStub = sinon.stub(
+ queryAndAssert<GrAccountEntry>(element, '#entry'),
+ 'getText'
+ );
getTextStub.onFirstCall().returns('');
getTextStub.onSecondCall().returns('test');
getTextStub.onThirdCall().returns('test@test');
// When entry is empty, return true.
- const clearStub = sinon.stub(element.entry!, 'clear');
+ const clearStub = sinon.stub(
+ queryAndAssert<GrAccountEntry>(element, '#entry'),
+ 'clear'
+ );
assert.isTrue(element.submitEntryText());
assert.isFalse(clearStub.called);
@@ -387,7 +399,9 @@
const acct = makeAccount();
handleAdd({account: acct, count: 1});
await element.updateComplete;
- assert.isTrue(element.entry!.hasAttribute('hidden'));
+ assert.isTrue(
+ queryAndAssert<GrAccountEntry>(element, '#entry').hasAttribute('hidden')
+ );
});
test('enter text calls suggestions provider', async () => {
@@ -410,8 +424,10 @@
'makeSuggestionItem'
);
- const input = element.entry!.$.input;
-
+ const input = queryAndAssert<GrAutocomplete>(
+ queryAndAssert<GrAccountEntry>(element, '#entry'),
+ '#input'
+ );
input.text = 'newTest';
MockInteractions.focus(input.$.input);
input.noDebounce = true;
@@ -446,7 +462,10 @@
suite('keyboard interactions', () => {
test('backspace at text input start removes last account', async () => {
- const input = element.entry!.$.input;
+ const input = queryAndAssert<GrAutocomplete>(
+ queryAndAssert<GrAccountEntry>(element, '#entry'),
+ '#input'
+ );
sinon.stub(input, '_updateSuggestions');
sinon.stub(element, 'computeRemovable').returns(true);
await await element.updateComplete;
@@ -472,7 +491,10 @@
});
test('arrow key navigation', async () => {
- const input = element.entry!.$.input;
+ const input = queryAndAssert<GrAutocomplete>(
+ queryAndAssert<GrAccountEntry>(element, '#entry'),
+ '#input'
+ );
input.text = '';
element.accounts = [makeAccount(), makeAccount()];
await element.updateComplete;