Migrate gr-download-commands to lit Change-Id: I27a6d786bdcd2d52272a34a28c72533048838571
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts index 5fc7bc8..a3f7374 100644 --- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts +++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts
@@ -246,7 +246,10 @@ )} .schemes=${this.schemes} .selectedScheme=${this.selectedScheme} - @selected-scheme-changed=${this.handleSelectedSchemeValueChanged} + @selected-scheme-changed=${(e: BindValueChangeEvent) => { + if (this.loading) return; + this.selectedScheme = e.detail.value; + }} ></gr-download-commands> </fieldset> </div> @@ -1121,12 +1124,7 @@ } } - private handleSelectedSchemeValueChanged(e: CustomEvent) { - if (this.loading) return; - this.selectedScheme = e.detail.value; - } - - private handleDescriptionTextChanged(e: CustomEvent) { + private handleDescriptionTextChanged(e: BindValueChangeEvent) { if (!this.repoConfig || this.loading) return; this.repoConfig = { ...this.repoConfig,
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts index cc7e54ac..a72749b 100644 --- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts +++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.ts
@@ -28,8 +28,8 @@ import {LitElement, PropertyValues, html, css} from 'lit'; import {customElement, property, state, query} from 'lit/decorators'; import {assertIsDefined} from '../../../utils/common-util'; -import {BindValueChangeEvent} from '../../../types/events'; import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs'; +import {BindValueChangeEvent} from '../../../types/events'; @customElement('gr-download-dialog') export class GrDownloadDialog extends LitElement {
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts index 6eb19da..64f97ca 100644 --- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts +++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.ts
@@ -18,53 +18,44 @@ import '@polymer/paper-tabs/paper-tab'; import '@polymer/paper-tabs/paper-tabs'; import '../gr-shell-command/gr-shell-command'; -import '../../../styles/gr-paper-styles'; -import '../../../styles/shared-styles'; -import {PolymerElement} from '@polymer/polymer/polymer-element'; -import {htmlTemplate} from './gr-download-commands_html'; -import {customElement, property} from '@polymer/decorators'; -import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs'; import {getAppContext} from '../../../services/app-context'; import {queryAndAssert} from '../../../utils/common-util'; import {GrShellCommand} from '../gr-shell-command/gr-shell-command'; +import {paperStyles} from '../../../styles/gr-paper-styles'; +import {sharedStyles} from '../../../styles/shared-styles'; +import {LitElement, html, css} from 'lit'; +import {customElement, property, state} from 'lit/decorators'; +import {fire} from '../../../utils/event-util'; +import {BindValueChangeEvent} from '../../../types/events'; declare global { interface HTMLElementEventMap { 'selected-changed': CustomEvent<{value: number}>; + 'selected-scheme-changed': BindValueChangeEvent; } interface HTMLElementTagNameMap { 'gr-download-commands': GrDownloadCommands; } } -export interface GrDownloadCommands { - $: { - downloadTabs: PaperTabsElement; - }; -} - export interface Command { title: string; command: string; } @customElement('gr-download-commands') -export class GrDownloadCommands extends PolymerElement { - static get template() { - return htmlTemplate; - } - +export class GrDownloadCommands extends LitElement { // TODO(TS): maybe default to [] as only used in dom-repeat @property({type: Array}) commands?: Command[]; - @property({type: Boolean}) - _loggedIn = false; + // private but used in test + @state() loggedIn = false; @property({type: Array}) schemes: string[] = []; - @property({type: String, notify: true}) + @property({type: String}) selectedScheme?: string; @property({type: Boolean}) @@ -79,14 +70,15 @@ override connectedCallback() { super.connectedCallback(); - this._getLoggedIn().then(loggedIn => { - this._loggedIn = loggedIn; + this.restApiService.getLoggedIn().then(loggedIn => { + this.loggedIn = loggedIn; }); this.subscriptions.push( this.userModel.preferences$.subscribe(prefs => { if (prefs?.download_scheme) { // Note (issue 5180): normalize the download scheme with lower-case. this.selectedScheme = prefs.download_scheme.toLowerCase(); + fire(this, 'selected-scheme-changed', {value: this.selectedScheme}); } }) ); @@ -100,42 +92,121 @@ super.disconnectedCallback(); } + static override get styles() { + return [ + paperStyles, + sharedStyles, + css` + paper-tabs { + height: 3rem; + margin-bottom: var(--spacing-m); + --paper-tabs-selection-bar-color: var(--link-color); + } + paper-tab { + max-width: 15rem; + text-transform: uppercase; + --paper-tab-ink: var(--link-color); + } + label, + input { + display: block; + } + label { + font-weight: var(--font-weight-bold); + } + .schemes { + display: flex; + justify-content: space-between; + } + .commands { + display: flex; + flex-direction: column; + } + gr-shell-command { + margin-bottom: var(--spacing-m); + } + .hidden { + display: none; + } + `, + ]; + } + + override render() { + return html` + <div class="schemes">${this.renderDownloadTabs()}</div> + ${this.renderCommands()} + `; + } + + private renderDownloadTabs() { + if (this.schemes.length <= 1) return; + + const selectedIndex = + this.schemes.findIndex(scheme => scheme === this.selectedScheme) || 0; + return html` + <paper-tabs + id="downloadTabs" + .selected=${selectedIndex} + @selected-changed=${this.handleTabChange} + > + ${this.schemes.map(scheme => this.renderPaperTab(scheme))} + </paper-tabs> + `; + } + + private renderPaperTab(scheme: string) { + return html` <paper-tab data-scheme=${scheme}>${scheme}</paper-tab> `; + } + + private renderCommands() { + if (!this.schemes.length) return; + + return html` + <div class="commands"> + ${this.commands?.map((command, index) => + this.renderShellCommand(command, index) + )} + </div> + `; + } + + private renderShellCommand(command: Command, index: number) { + return html` + <gr-shell-command + class="${this.computeClass(command.title)}" + .label=${command.title} + .command=${command.command} + .tooltip=${this.computeTooltip(index)} + ></gr-shell-command> + `; + } + focusOnCopy() { queryAndAssert<GrShellCommand>(this, 'gr-shell-command').focusOnCopy(); } - _getLoggedIn() { - return this.restApiService.getLoggedIn(); - } - - _handleTabChange(e: CustomEvent<{value: number}>) { + private handleTabChange = (e: CustomEvent<{value: number}>) => { const scheme = this.schemes[e.detail.value]; if (scheme && scheme !== this.selectedScheme) { - this.set('selectedScheme', scheme); - if (this._loggedIn) { + this.selectedScheme = scheme; + fire(this, 'selected-scheme-changed', {value: scheme}); + if (this.loggedIn) { this.userModel.updatePreferences({ download_scheme: this.selectedScheme, }); } } - } + }; - _computeSelected(schemes: string[], selectedScheme?: string) { - return `${schemes.findIndex(scheme => scheme === selectedScheme) || 0}`; - } - - _computeShowTabs(schemes: string[]) { - return schemes.length > 1 ? '' : 'hidden'; - } - - _computeTooltip(showKeyboardShortcutTooltips: boolean, index: number) { - return index <= 4 && showKeyboardShortcutTooltips + private computeTooltip(index: number) { + return index <= 4 && this.showKeyboardShortcutTooltips ? `Keyboard shortcut: ${index + 1}` : ''; } // TODO: maybe unify with strToClassName from dom-util - _computeClass(title: string) { + private computeClass(title: string) { // Only retain [a-z] chars, so "Cherry Pick" becomes "cherrypick". return '_label_' + title.replace(/[^a-z]+/gi, '').toLowerCase(); }
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts deleted file mode 100644 index f9c08ba..0000000 --- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_html.ts +++ /dev/null
@@ -1,78 +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-paper-styles"> - /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */ - </style> - <style include="shared-styles"> - paper-tabs { - height: 3rem; - margin-bottom: var(--spacing-m); - --paper-tabs-selection-bar-color: var(--link-color); - } - paper-tab { - max-width: 15rem; - text-transform: uppercase; - --paper-tab-ink: var(--link-color); - } - label, - input { - display: block; - } - label { - font-weight: var(--font-weight-bold); - } - .schemes { - display: flex; - justify-content: space-between; - } - .commands { - display: flex; - flex-direction: column; - } - gr-shell-command { - margin-bottom: var(--spacing-m); - } - .hidden { - display: none; - } - </style> - <div class="schemes"> - <paper-tabs - id="downloadTabs" - class$="[[_computeShowTabs(schemes)]]" - selected="[[_computeSelected(schemes, selectedScheme)]]" - on-selected-changed="_handleTabChange" - > - <template is="dom-repeat" items="[[schemes]]" as="scheme"> - <paper-tab data-scheme$="[[scheme]]">[[scheme]]</paper-tab> - </template> - </paper-tabs> - </div> - <div class="commands" hidden$="[[!schemes.length]]" hidden=""> - <template is="dom-repeat" items="[[commands]]" as="command" indexAs="index"> - <gr-shell-command - class$="[[_computeClass(command.title)]]" - label="[[command.title]]" - command="[[command.command]]" - tooltip="[[_computeTooltip(showKeyboardShortcutTooltips, index)]]" - ></gr-shell-command> - </template> - </div> -`;
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts index bd0ca70..eb9490f 100644 --- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts +++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.ts
@@ -18,11 +18,12 @@ import '../../../test/common-test-setup-karma'; import './gr-download-commands'; import {GrDownloadCommands} from './gr-download-commands'; -import {isHidden, queryAndAssert, stubRestApi} from '../../../test/test-utils'; +import {query, queryAndAssert, stubRestApi} from '../../../test/test-utils'; import {createPreferences} from '../../../test/test-data-generators'; import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions'; import {GrShellCommand} from '../gr-shell-command/gr-shell-command'; import {createDefaultPreferences} from '../../../constants/constants'; +import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs'; const basicFixture = fixtureFromElement('gr-download-commands'); @@ -63,7 +64,7 @@ element.schemes = SCHEMES; element.commands = COMMANDS; element.selectedScheme = SELECTED_SCHEME; - await flush(); + await element.updateComplete; }); test('focusOnCopy', () => { @@ -75,30 +76,37 @@ assert.isTrue(focusStub.called); }); - test('element visibility', () => { - assert.isFalse(isHidden(queryAndAssert(element, 'paper-tabs'))); - assert.isFalse(isHidden(queryAndAssert(element, '.commands'))); + test('element visibility', async () => { + assert.isTrue(Boolean(query(element, 'paper-tabs'))); + assert.isTrue(Boolean(query(element, '.commands'))); element.schemes = []; - assert.isTrue(isHidden(queryAndAssert(element, 'paper-tabs'))); - assert.isTrue(isHidden(queryAndAssert(element, '.commands'))); + await element.updateComplete; + assert.isFalse(Boolean(query(element, 'paper-tabs'))); + assert.isFalse(Boolean(query(element, '.commands'))); }); - test('tab selection', () => { - assert.equal(element.$.downloadTabs.selected, '0'); + test('tab selection', async () => { + assert.equal( + queryAndAssert<PaperTabsElement>(element, '#downloadTabs').selected, + '0' + ); MockInteractions.tap(queryAndAssert(element, '[data-scheme="ssh"]')); - flush(); + await element.updateComplete; assert.equal(element.selectedScheme, 'ssh'); - assert.equal(element.$.downloadTabs.selected, '2'); + assert.equal( + queryAndAssert<PaperTabsElement>(element, '#downloadTabs').selected, + '2' + ); }); - test('saves scheme to preferences', () => { - element._loggedIn = true; + test('saves scheme to preferences', async () => { + element.loggedIn = true; const savePrefsStub = stubRestApi('savePreferences').returns( Promise.resolve(createDefaultPreferences()) ); - flush(); + await element.updateComplete; const repoTab = queryAndAssert(element, 'paper-tab[data-scheme="repo"]'); @@ -114,7 +122,7 @@ suite('authenticated', () => { test('loads scheme from preferences', async () => { const element = basicFixture.instantiate(); - await flush(); + await element.updateComplete; element.userModel.setPreferences({ ...createPreferences(), download_scheme: 'repo', @@ -124,7 +132,7 @@ test('normalize scheme from preferences', async () => { const element = basicFixture.instantiate(); - await flush(); + await element.updateComplete; element.userModel.setPreferences({ ...createPreferences(), download_scheme: 'REPO',