Migrate gr-menu-editor to lit
Release-Notes: skip
Change-Id: I3ec78bf4eda22966921b48211fb6c400678f6915
diff --git a/polygerrit-ui/app/constants/constants.ts b/polygerrit-ui/app/constants/constants.ts
index 01ff6ce..8cdd765 100644
--- a/polygerrit-ui/app/constants/constants.ts
+++ b/polygerrit-ui/app/constants/constants.ts
@@ -260,14 +260,19 @@
NONE = 'NONE',
}
-// TODO(TS): Many properties are omitted here, but they are required.
-// Add default values for missing properties.
-export function createDefaultPreferences() {
+export function createDefaultPreferences(): PreferencesInfo {
return {
changes_per_page: 25,
diff_view: DiffViewMode.SIDE_BY_SIDE,
size_bar_in_change_table: true,
- } as PreferencesInfo;
+ my: [],
+ theme: AppTheme.LIGHT,
+ date_format: DateFormat.EURO,
+ time_format: TimeFormat.HHMM_24,
+ change_table: [],
+ email_strategy: EmailStrategy.ATTENTION_SET_ONLY,
+ default_base_for_merges: DefaultBase.AUTO_MERGE,
+ };
}
// These defaults should match the defaults in
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 e6ca58f..845b30c 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
@@ -1,48 +1,53 @@
/**
* @license
- * Copyright (C) 2016 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.
+ * Copyright 2016 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
*/
import '@polymer/iron-input/iron-input';
import '../../shared/gr-button/gr-button';
-import '../../../styles/shared-styles';
-import '../../../styles/gr-form-styles';
-import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-menu-editor_html';
-import {customElement, property} from '@polymer/decorators';
-import {TopMenuItemInfo} from '../../../types/common';
+import {PreferencesInfo, TopMenuItemInfo} from '../../../types/common';
+import {css, html, LitElement} from 'lit';
import {sharedStyles} from '../../../styles/shared-styles';
-import {css, html} from 'lit';
+import {formStyles} from '../../../styles/gr-form-styles';
+import {state, customElement} from 'lit/decorators';
+import {BindValueChangeEvent} from '../../../types/events';
+import {subscribe} from '../../lit/subscription-controller';
+import {getAppContext} from '../../../services/app-context';
+import {deepEqual} from '../../../utils/deep-util';
+import {createDefaultPreferences} from '../../../constants/constants';
+import {fontStyles} from '../../../styles/gr-font-styles';
+import {classMap} from 'lit/directives/class-map';
+import {menuPageStyles} from '../../../styles/gr-menu-page-styles';
@customElement('gr-menu-editor')
-export class GrMenuEditor extends PolymerElement {
- static get template() {
- return htmlTemplate;
+export class GrMenuEditor extends LitElement {
+ @state()
+ menuItems: TopMenuItemInfo[] = [];
+
+ @state()
+ originalPrefs: PreferencesInfo = createDefaultPreferences();
+
+ @state()
+ newName = '';
+
+ @state()
+ newUrl = '';
+
+ private readonly userModel = getAppContext().userModel;
+
+ override connectedCallback() {
+ super.connectedCallback();
+ subscribe(this, this.userModel.preferences$, prefs => {
+ this.originalPrefs = prefs;
+ this.menuItems = [...prefs.my];
+ });
}
- @property({type: Array})
- menuItems!: TopMenuItemInfo[];
-
- @property({type: String})
- _newName?: string;
-
- @property({type: String})
- _newUrl?: string;
-
- styles = [
+ static override styles = [
+ formStyles,
sharedStyles,
+ fontStyles,
+ menuPageStyles,
css`
.buttonColumn {
width: 2em;
@@ -64,154 +69,167 @@
`,
];
- render() {
+ override render() {
+ const unchanged = deepEqual(this.menuItems, this.originalPrefs.my);
+ const classes = {
+ 'heading-2': true,
+ edited: !unchanged,
+ };
return html`
<div class="gr-form-styles">
- <table>
- <thead>
- <tr>
- <th class="nameHeader">Name</th>
- <th class="url-header">URL</th>
- </tr>
- </thead>
- <tbody>
- <template is="dom-repeat" items="[[menuItems]]">
+ <h2 id="Menu" class=${classMap(classes)}>Menu</h2>
+ <fieldset id="menu">
+ <table>
+ <thead>
<tr>
- <td>[[item.name]]</td>
- <td class="urlCell">[[item.url]]</td>
- <td class="buttonColumn">
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleMoveUpButton"
- class="moveUpButton"
- >↑</gr-button
- >
- </td>
- <td class="buttonColumn">
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleMoveDownButton"
- class="moveDownButton"
- >↓</gr-button
- >
- </td>
- <td>
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleDeleteButton"
- class="remove-button"
- >Delete</gr-button
- >
- </td>
+ <th>Name</th>
+ <th>URL</th>
</tr>
- </template>
- </tbody>
- <tfoot>
- <tr>
- <th>
- <iron-input
- placeholder="New Title"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newName}}"
- >
- <input
- is="iron-input"
- placeholder="New Title"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newName}}"
- />
- </iron-input>
- </th>
- <th>
- <iron-input
- class="newUrlInput"
- placeholder="New URL"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newUrl}}"
- >
- <input
- class="newUrlInput"
- is="iron-input"
- placeholder="New URL"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newUrl}}"
- />
- </iron-input>
- </th>
- <th></th>
- <th></th>
- <th>
- <gr-button
- link=""
- disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
- on-click="_handleAddButton"
- >Add</gr-button
- >
- </th>
- </tr>
- </tfoot>
- </table>
+ </thead>
+ <tbody>
+ ${this.menuItems.map((item, index) =>
+ this.renderMenuItemRow(item, index)
+ )}
+ </tbody>
+ <tfoot>
+ ${this.renderFooterRow()}
+ </tfoot>
+ </table>
+ <gr-button id="save" @click=${this.handleSave} ?disabled=${unchanged}
+ >Save changes</gr-button
+ >
+ <gr-button id="reset" link @click=${this.handleReset}
+ >Reset</gr-button
+ >
+ </fieldset>
</div>
`;
}
- _handleMoveUpButton(e: Event) {
- const target = (dom(e) as EventApi).localTarget;
- if (!(target instanceof HTMLElement)) return;
- const index = Number(target.dataset['index']);
- if (index === 0) {
- return;
- }
- const row = this.menuItems[index];
- const prev = this.menuItems[index - 1];
- this.splice('menuItems', index - 1, 2, row, prev);
+ private renderMenuItemRow(item: TopMenuItemInfo, index: number) {
+ return html`
+ <tr>
+ <td>${item.name}</td>
+ <td class="urlCell">${item.url}</td>
+ <td class="buttonColumn">
+ <gr-button
+ link
+ data-index=${index}
+ @click=${() => this.swapItems(index, index - 1)}
+ class="moveUpButton"
+ >↑</gr-button
+ >
+ </td>
+ <td class="buttonColumn">
+ <gr-button
+ link
+ data-index=${index}
+ @click=${() => this.swapItems(index, index + 1)}
+ class="moveDownButton"
+ >↓</gr-button
+ >
+ </td>
+ <td>
+ <gr-button
+ link
+ data-index=${index}
+ @click=${() => {
+ this.menuItems.splice(index, 1);
+ this.requestUpdate('menuItems');
+ }}
+ class="remove-button"
+ >Delete</gr-button
+ >
+ </td>
+ </tr>
+ `;
}
- _handleMoveDownButton(e: Event) {
- const target = (dom(e) as EventApi).localTarget;
- if (!(target instanceof HTMLElement)) return;
- const index = Number(target.dataset['index']);
- if (index === this.menuItems.length - 1) {
- return;
- }
- const row = this.menuItems[index];
- const next = this.menuItems[index + 1];
- this.splice('menuItems', index, 2, next, row);
+ private renderFooterRow() {
+ return html`
+ <tr>
+ <th>
+ <iron-input
+ .bindValue=${this.newName}
+ @bind-value-changed=${(e: BindValueChangeEvent) => {
+ this.newName = e.detail.value ?? '';
+ }}
+ >
+ <input
+ is="iron-input"
+ placeholder="New Title"
+ @keydown=${this.handleInputKeydown}
+ />
+ </iron-input>
+ </th>
+ <th>
+ <iron-input
+ .bindValue=${this.newUrl}
+ @bind-value-changed=${(e: BindValueChangeEvent) => {
+ this.newUrl = e.detail.value ?? '';
+ }}
+ >
+ <input
+ class="newUrlInput"
+ placeholder="New URL"
+ @keydown=${this.handleInputKeydown}
+ />
+ </iron-input>
+ </th>
+ <th></th>
+ <th></th>
+ <th>
+ <gr-button
+ id="add"
+ link
+ ?disabled=${this.newName.length === 0 || this.newUrl.length === 0}
+ @click=${this.handleAddButton}
+ >Add</gr-button
+ >
+ </th>
+ </tr>
+ `;
}
- _handleDeleteButton(e: Event) {
- const target = (dom(e) as EventApi).localTarget;
- if (!(target instanceof HTMLElement)) return;
- const index = Number(target.dataset['index']);
- this.splice('menuItems', index, 1);
+ private handleSave() {
+ this.userModel.updatePreferences({
+ ...this.originalPrefs,
+ my: this.menuItems,
+ });
}
- _handleAddButton() {
- if (this._computeAddDisabled(this._newName, this._newUrl)) {
- return;
- }
+ private handleReset() {
+ this.menuItems = [...this.originalPrefs.my];
+ }
- this.splice('menuItems', this.menuItems.length, 0, {
- name: this._newName,
- url: this._newUrl,
+ private swapItems(i: number, j: number) {
+ const max = this.menuItems.length - 1;
+ if (i < 0 || j < 0) return;
+ if (i > max || j > max) return;
+ const x = this.menuItems[i];
+ this.menuItems[i] = this.menuItems[j];
+ this.menuItems[j] = x;
+ this.requestUpdate('menuItems');
+ }
+
+ // visible for testing
+ handleAddButton() {
+ if (this.newName.length === 0 || this.newUrl.length === 0) return;
+
+ this.menuItems.push({
+ name: this.newName,
+ url: this.newUrl,
target: '_blank',
});
-
- this._newName = '';
- this._newUrl = '';
+ this.newName = '';
+ this.newUrl = '';
+ this.requestUpdate('menuItems');
}
- _computeAddDisabled(newName?: string, newUrl?: string) {
- return !newName?.length || !newUrl?.length;
- }
-
- _handleInputKeydown(e: KeyboardEvent) {
+ private handleInputKeydown(e: KeyboardEvent) {
if (e.keyCode === 13) {
e.stopPropagation();
- this._handleAddButton();
+ this.handleAddButton();
}
}
}
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts
deleted file mode 100644
index e4d66e2..0000000
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_html.ts
+++ /dev/null
@@ -1,131 +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">
- .buttonColumn {
- width: 2em;
- }
- .moveUpButton,
- .moveDownButton {
- width: 100%;
- }
- tbody tr:first-of-type td .moveUpButton,
- tbody tr:last-of-type td .moveDownButton {
- display: none;
- }
- td.urlCell {
- word-break: break-word;
- }
- .newUrlInput {
- min-width: 23em;
- }
- </style>
- <style include="gr-form-styles">
- /* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
- </style>
- <div class="gr-form-styles">
- <table>
- <thead>
- <tr>
- <th class="nameHeader">Name</th>
- <th class="url-header">URL</th>
- </tr>
- </thead>
- <tbody>
- <template is="dom-repeat" items="[[menuItems]]">
- <tr>
- <td>[[item.name]]</td>
- <td class="urlCell">[[item.url]]</td>
- <td class="buttonColumn">
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleMoveUpButton"
- class="moveUpButton"
- >↑</gr-button
- >
- </td>
- <td class="buttonColumn">
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleMoveDownButton"
- class="moveDownButton"
- >↓</gr-button
- >
- </td>
- <td>
- <gr-button
- link=""
- data-index$="[[index]]"
- on-click="_handleDeleteButton"
- class="remove-button"
- >Delete</gr-button
- >
- </td>
- </tr>
- </template>
- </tbody>
- <tfoot>
- <tr>
- <th>
- <iron-input
- placeholder="New Title"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newName}}"
- >
- <input
- is="iron-input"
- placeholder="New Title"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newName}}"
- />
- </iron-input>
- </th>
- <th>
- <iron-input
- class="newUrlInput"
- placeholder="New URL"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newUrl}}"
- >
- <input
- class="newUrlInput"
- is="iron-input"
- placeholder="New URL"
- on-keydown="_handleInputKeydown"
- bind-value="{{_newUrl}}"
- />
- </iron-input>
- </th>
- <th></th>
- <th></th>
- <th>
- <gr-button
- link=""
- disabled$="[[_computeAddDisabled(_newName, _newUrl)]]"
- on-click="_handleAddButton"
- >Add</gr-button
- >
- </th>
- </tr>
- </tfoot>
- </table>
- </div>
-`;
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.ts b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.ts
index 9785ccb..c6130df 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.ts
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.ts
@@ -1,29 +1,18 @@
/**
* @license
- * Copyright (C) 2016 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.
+ * Copyright 2016 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
*/
-
import '../../../test/common-test-setup-karma';
import './gr-menu-editor';
import {GrMenuEditor} from './gr-menu-editor';
import * as MockInteractions from '@polymer/iron-test-helpers/mock-interactions';
-import {query, queryAll} from '../../../test/test-utils';
+import {query, queryAndAssert, waitUntil} from '../../../test/test-utils';
import {PaperButtonElement} from '@polymer/paper-button';
import {TopMenuItemInfo} from '../../../types/common';
-
-const basicFixture = fixtureFromElement('gr-menu-editor');
+import {GrButton} from '../../shared/gr-button/gr-button';
+import {fixture, html} from '@open-wc/testing-helpers';
+import {createDefaultPreferences} from '../../../constants/constants';
suite('gr-menu-editor tests', () => {
let element: GrMenuEditor;
@@ -53,52 +42,229 @@
}
setup(async () => {
- element = basicFixture.instantiate();
+ element = await fixture<GrMenuEditor>(
+ html`<gr-menu-editor></gr-menu-editor>`
+ );
menu = [
{url: '/first/url', name: 'first name', target: '_blank'},
{url: '/second/url', name: 'second name', target: '_blank'},
{url: '/third/url', name: 'third name', target: '_blank'},
];
- element.set('menuItems', menu);
- await flush();
+ element.originalPrefs = {...createDefaultPreferences(), my: menu};
+ element.menuItems = [...menu];
+ await element.updateComplete;
});
test('renders', () => {
- const rows = queryAll(query<HTMLElement>(element, 'tbody')!, 'tr');
- let tds;
-
- assert.equal(rows.length, menu.length);
- for (let i = 0; i < menu.length; i++) {
- tds = rows[i].querySelectorAll('td');
- assert.equal(tds[0].textContent, menu[i].name);
- assert.equal(tds[1].textContent, menu[i].url);
- }
-
- assert.isTrue(
- element._computeAddDisabled(element._newName, element._newUrl)
- );
+ expect(element).shadowDom.to.equal(/* HTML */ `
+ <div class="gr-form-styles">
+ <h2 class="heading-2" id="Menu">Menu</h2>
+ <fieldset id="menu">
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>URL</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>first name</td>
+ <td class="urlCell">/first/url</td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveUpButton"
+ data-index="0"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↑
+ </gr-button>
+ </td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveDownButton"
+ data-index="0"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↓
+ </gr-button>
+ </td>
+ <td>
+ <gr-button
+ aria-disabled="false"
+ class="remove-button"
+ data-index="0"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ Delete
+ </gr-button>
+ </td>
+ </tr>
+ <tr>
+ <td>second name</td>
+ <td class="urlCell">/second/url</td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveUpButton"
+ data-index="1"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↑
+ </gr-button>
+ </td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveDownButton"
+ data-index="1"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↓
+ </gr-button>
+ </td>
+ <td>
+ <gr-button
+ aria-disabled="false"
+ class="remove-button"
+ data-index="1"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ Delete
+ </gr-button>
+ </td>
+ </tr>
+ <tr>
+ <td>third name</td>
+ <td class="urlCell">/third/url</td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveUpButton"
+ data-index="2"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↑
+ </gr-button>
+ </td>
+ <td class="buttonColumn">
+ <gr-button
+ aria-disabled="false"
+ class="moveDownButton"
+ data-index="2"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ ↓
+ </gr-button>
+ </td>
+ <td>
+ <gr-button
+ aria-disabled="false"
+ class="remove-button"
+ data-index="2"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ Delete
+ </gr-button>
+ </td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th>
+ <iron-input>
+ <input is="iron-input" placeholder="New Title" />
+ </iron-input>
+ </th>
+ <th>
+ <iron-input>
+ <input class="newUrlInput" placeholder="New URL" />
+ </iron-input>
+ </th>
+ <th></th>
+ <th></th>
+ <th>
+ <gr-button
+ aria-disabled="true"
+ disabled=""
+ id="add"
+ link=""
+ role="button"
+ tabindex="-1"
+ >
+ Add
+ </gr-button>
+ </th>
+ </tr>
+ </tfoot>
+ </table>
+ <gr-button
+ aria-disabled="true"
+ disabled=""
+ id="save"
+ role="button"
+ tabindex="-1"
+ >
+ Save changes
+ </gr-button>
+ <gr-button
+ aria-disabled="false"
+ id="reset"
+ link=""
+ role="button"
+ tabindex="0"
+ >
+ Reset
+ </gr-button>
+ </fieldset>
+ </div>
+ `);
});
- test('_computeAddDisabled', () => {
- assert.isTrue(element._computeAddDisabled('', ''));
- assert.isTrue(element._computeAddDisabled('name', ''));
- assert.isTrue(element._computeAddDisabled('', 'url'));
- assert.isFalse(element._computeAddDisabled('name', 'url'));
+ test('add button disabled', async () => {
+ element.newName = 'test-name';
+ await element.updateComplete;
+ let addButton = queryAndAssert<GrButton>(element, 'gr-button#add');
+ assert.isTrue(addButton.hasAttribute('disabled'));
+
+ element.newUrl = 'test-url';
+ await element.updateComplete;
+ addButton = queryAndAssert<GrButton>(element, 'gr-button#add');
+ assert.isFalse(addButton.hasAttribute('disabled'));
});
- test('add a new menu item', () => {
+ test('add a new menu item', async () => {
const newName = 'new name';
const newUrl = 'new url';
-
- element._newName = newName;
- element._newUrl = newUrl;
- assert.isFalse(
- element._computeAddDisabled(element._newName, element._newUrl)
- );
-
const originalMenuLength = element.menuItems.length;
- element._handleAddButton();
+ element.newName = newName;
+ element.newUrl = newUrl;
+ await element.updateComplete;
+
+ const addButton = queryAndAssert<GrButton>(element, 'gr-button#add');
+ assert.isFalse(addButton.hasAttribute('disabled'));
+ addButton.click();
assert.equal(element.menuItems.length, originalMenuLength + 1);
assert.equal(element.menuItems[element.menuItems.length - 1].name, newName);
@@ -117,6 +283,37 @@
assertMenuNamesEqual(element, ['first name', 'third name', 'second name']);
});
+ test('move item down and save', async () => {
+ assertMenuNamesEqual(element, ['first name', 'second name', 'third name']);
+ const saveButton = queryAndAssert<GrButton>(element, 'gr-button#save');
+ assert.isTrue(saveButton.hasAttribute('disabled'));
+
+ move(element, 1, 'Down');
+ await element.updateComplete;
+ assertMenuNamesEqual(element, ['first name', 'third name', 'second name']);
+ assert.isFalse(saveButton.hasAttribute('disabled'));
+
+ saveButton.click();
+ await waitUntil(() => element.originalPrefs.my[1].name === 'third name');
+ await element.updateComplete;
+
+ assertMenuNamesEqual(element, ['first name', 'third name', 'second name']);
+ assert.isTrue(saveButton.hasAttribute('disabled'));
+ });
+
+ test('move item down and reset', async () => {
+ assertMenuNamesEqual(element, ['first name', 'second name', 'third name']);
+
+ move(element, 1, 'Down');
+ assertMenuNamesEqual(element, ['first name', 'third name', 'second name']);
+
+ const resetButton = queryAndAssert<GrButton>(element, 'gr-button#reset');
+ resetButton.click();
+ await element.updateComplete;
+
+ assertMenuNamesEqual(element, ['first name', 'second name', 'third name']);
+ });
+
test('move items up', () => {
assertMenuNamesEqual(element, ['first name', 'second name', 'third name']);
@@ -161,9 +358,9 @@
assertMenuNamesEqual(element, []);
// Add item to empty menu.
- element._newName = 'new name';
- element._newUrl = 'new url';
- element._handleAddButton();
+ element.newName = 'new name';
+ element.newUrl = 'new url';
+ element.handleAddButton();
assertMenuNamesEqual(element, ['new name']);
});
});
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 fb60bf8..1218dfd 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
@@ -25,7 +25,6 @@
import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
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-diff-preferences/gr-diff-preferences';
import '../../shared/gr-page-nav/gr-page-nav';
import '../../shared/gr-select/gr-select';
@@ -50,11 +49,7 @@
import {GrGroupList} from '../gr-group-list/gr-group-list';
import {GrIdentities} from '../gr-identities/gr-identities';
import {GrDiffPreferences} from '../../shared/gr-diff-preferences/gr-diff-preferences';
-import {
- PreferencesInput,
- ServerInfo,
- TopMenuItemInfo,
-} from '../../../types/common';
+import {PreferencesInput, ServerInfo} from '../../../types/common';
import {GrSshEditor} from '../gr-ssh-editor/gr-ssh-editor';
import {GrGpgEditor} from '../gr-gpg-editor/gr-gpg-editor';
import {GrEmailEditor} from '../gr-email-editor/gr-email-editor';
@@ -103,8 +98,6 @@
LocalPrefsToPrefs,
}
-type LocalMenuItemInfo = Omit<TopMenuItemInfo, 'id'>;
-
export interface GrSettingsView {
$: {
accountInfo: GrAccountInfo;
@@ -129,8 +122,6 @@
emailFormatSelect: HTMLInputElement;
defaultBaseForMergesSelect: HTMLInputElement;
diffViewSelect: HTMLInputElement;
- menu: HTMLFieldSetElement;
- resetButton: GrButton;
};
}
@@ -167,9 +158,6 @@
@property({type: Array})
_localChangeTableColumns: string[] = [];
- @property({type: Array})
- _localMenu: LocalMenuItemInfo[] = [];
-
@property({type: Boolean})
_loading = true;
@@ -183,9 +171,6 @@
_diffPrefsChanged = false;
@property({type: Boolean})
- _menuChanged = false;
-
- @property({type: Boolean})
_watchedProjectsChanged = false;
@property({type: Boolean})
@@ -247,7 +232,6 @@
this.prefs = prefs;
this._showNumber = !!prefs.legacycid_in_change_table;
this._copyPrefs(CopyPrefsDirection.PrefsToLocalPrefs);
- this._localMenu = this._cloneMenu(prefs.my);
this._localChangeTableColumns =
prefs.change_table.length === 0
? columnNames
@@ -355,11 +339,6 @@
}
}
- _cloneMenu(prefs: TopMenuItemInfo[]) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- return prefs.map(({id, ...item}) => item);
- }
-
@observe('_localChangeTableColumns', '_showNumber')
_handleChangeTableChanged() {
if (this._isLoading()) {
@@ -422,14 +401,6 @@
this.set('_localPrefs.signed_off_by', this.$.insertSignedOff.checked);
}
- @observe('_localMenu.splices')
- _handleMenuChanged() {
- if (this._isLoading()) {
- return;
- }
- this._menuChanged = true;
- }
-
_handleSaveAccountInfo() {
this.$.accountInfo.save();
}
@@ -454,21 +425,6 @@
this.$.diffPrefs.save();
}
- _handleSaveMenu() {
- this.set('prefs.my', this._localMenu);
- return this.restApiService.savePreferences(this.prefs).then(() => {
- this._menuChanged = false;
- });
- }
-
- _handleResetMenuButton() {
- return this.restApiService.getDefaultPreferences().then(data => {
- if (data?.my) {
- this._localMenu = this._cloneMenu(data.my);
- }
- });
- }
-
_handleSaveWatchedProjects() {
this.$.watchedProjectsEditor.save();
}
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
index e50b2de..6804470 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
@@ -361,19 +361,7 @@
>
</fieldset>
<gr-edit-preferences id="editPrefs"></gr-edit-preferences>
- <h2 id="Menu" class$="[[_computeHeaderClass(_menuChanged)]]">Menu</h2>
- <fieldset id="menu">
- <gr-menu-editor menu-items="{{_localMenu}}"></gr-menu-editor>
- <gr-button
- id="saveMenu"
- on-click="_handleSaveMenu"
- disabled="[[!_menuChanged]]"
- >Save changes</gr-button
- >
- <gr-button id="resetButton" link="" on-click="_handleResetMenuButton"
- >Reset</gr-button
- >
- </fieldset>
+ <gr-menu-editor></gr-menu-editor>
<h2
id="ChangeTableColumns"
class$="[[_computeHeaderClass(_changeTableChanged)]]"
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
index 8049124..7f81f42 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
@@ -19,7 +19,7 @@
import './gr-settings-view';
import {GrSettingsView} from './gr-settings-view';
import {GerritView} from '../../../services/router/router-model';
-import {queryAll, queryAndAssert, stubRestApi} from '../../../test/test-utils';
+import {queryAndAssert, stubRestApi} from '../../../test/test-utils';
import {
AuthInfo,
AccountDetailInfo,
@@ -259,7 +259,6 @@
);
assert.isFalse(element._prefsChanged);
- assert.isFalse(element._menuChanged);
const publishOnPush = valueOf('Publish comments on push', 'preferences')!
.firstElementChild!;
@@ -267,7 +266,6 @@
MockInteractions.tap(publishOnPush);
assert.isTrue(element._prefsChanged);
- assert.isFalse(element._menuChanged);
stubRestApi('savePreferences').callsFake(prefs => {
assertMenusEqual(prefs.my, preferences.my);
@@ -278,7 +276,6 @@
// Save the change.
await element._handleSavePreferences();
assert.isFalse(element._prefsChanged);
- assert.isFalse(element._menuChanged);
});
test('publish comments on push', async () => {
@@ -288,7 +285,6 @@
)!.firstElementChild!;
MockInteractions.tap(publishCommentsOnPush);
- assert.isFalse(element._menuChanged);
assert.isTrue(element._prefsChanged);
stubRestApi('savePreferences').callsFake(prefs => {
@@ -299,7 +295,6 @@
// Save the change.
await element._handleSavePreferences();
assert.isFalse(element._prefsChanged);
- assert.isFalse(element._menuChanged);
});
test('set new changes work-in-progress', async () => {
@@ -309,7 +304,6 @@
)!.firstElementChild!;
MockInteractions.tap(newChangesWorkInProgress);
- assert.isFalse(element._menuChanged);
assert.isTrue(element._prefsChanged);
stubRestApi('savePreferences').callsFake(prefs => {
@@ -320,40 +314,6 @@
// Save the change.
await element._handleSavePreferences();
assert.isFalse(element._prefsChanged);
- assert.isFalse(element._menuChanged);
- });
-
- test('menu', async () => {
- assert.isFalse(element._menuChanged);
- assert.isFalse(element._prefsChanged);
-
- assertMenusEqual(element._localMenu, preferences.my);
-
- const menu = element.$.menu.firstElementChild!;
- let tableRows = queryAll(menu, 'tbody tr');
- // let tableRows = menu.root.querySelectorAll('tbody tr');
- assert.equal(tableRows.length, preferences.my.length);
-
- // Add a menu item:
- element.splice('_localMenu', 1, 0, {name: 'foo', url: 'bar', target: ''});
- flush();
-
- // tableRows = menu.root.querySelectorAll('tbody tr');
- tableRows = queryAll(menu, 'tbody tr');
- assert.equal(tableRows.length, preferences.my.length + 1);
-
- assert.isTrue(element._menuChanged);
- assert.isFalse(element._prefsChanged);
-
- stubRestApi('savePreferences').callsFake(prefs => {
- assertMenusEqual(prefs.my, element._localMenu);
- return Promise.resolve(createDefaultPreferences());
- });
-
- await element._handleSaveMenu();
- assert.isFalse(element._menuChanged);
- assert.isFalse(element._prefsChanged);
- assertMenusEqual(element.prefs.my, element._localMenu);
});
test('add email validation', () => {
@@ -445,39 +405,6 @@
assert.isTrue(element.prefs.legacycid_in_change_table);
});
- test('reset menu item back to default', async () => {
- const originalMenu = {
- ...createDefaultPreferences(),
- my: [
- {url: '/first/url', name: 'first name', target: '_blank'},
- {url: '/second/url', name: 'second name', target: '_blank'},
- {url: '/third/url', name: 'third name', target: '_blank'},
- ] as TopMenuItemInfo[],
- };
-
- stubRestApi('getDefaultPreferences').returns(Promise.resolve(originalMenu));
-
- const updatedMenu = [
- {url: '/first/url', name: 'first name', target: '_blank'},
- {url: '/second/url', name: 'second name', target: '_blank'},
- {url: '/third/url', name: 'third name', target: '_blank'},
- {url: '/fourth/url', name: 'fourth name', target: '_blank'},
- ];
-
- element.set('_localMenu', updatedMenu);
-
- await element._handleResetMenuButton();
- assertMenusEqual(element._localMenu, originalMenu.my);
- });
-
- test('test that reset button is called', () => {
- const overlayOpen = sinon.stub(element, '_handleResetMenuButton');
-
- MockInteractions.tap(element.$.resetButton);
-
- assert.isTrue(overlayOpen.called);
- });
-
test('_showHttpAuth', () => {
const serverConfig: ServerInfo = {
...createServerInfo(),
diff --git a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
index ebbdc9c..d91b438 100644
--- a/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
+++ b/polygerrit-ui/app/test/mocks/gr-rest-api_mock.ts
@@ -69,6 +69,7 @@
GroupName,
UrlEncodedRepoName,
NumericChangeId,
+ PreferencesInput,
} from '../../types/common';
import {DiffInfo, DiffPreferencesInfo} from '../../types/diff';
import {readResponsePayload} from '../../elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
@@ -84,7 +85,6 @@
import {
createDefaultDiffPrefs,
createDefaultEditPrefs,
- createDefaultPreferences,
} from '../../constants/constants';
import {ParsedChangeInfo} from '../../types/types';
@@ -502,8 +502,9 @@
saveIncludedGroup(): Promise<GroupInfo | undefined> {
throw new Error('saveIncludedGroup() not implemented by RestApiMock.');
},
- savePreferences(): Promise<PreferencesInfo> {
- return Promise.resolve(createDefaultPreferences());
+ savePreferences(input: PreferencesInput): Promise<PreferencesInfo> {
+ const info = input as PreferencesInfo;
+ return Promise.resolve({...info});
},
saveRepoConfig(): Promise<Response> {
return Promise.resolve(new Response());