Merge "Revert "Revert "Add keyboard shortcuts for admin menu links"""
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
index 27fe620..d3562f2 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
@@ -16,7 +16,11 @@
import {LitElement, PropertyValues, css, html} from 'lit';
import {customElement, query, property, state} from 'lit/decorators.js';
import {assertIsDefined} from '../../../utils/common-util';
-import {AdminViewState} from '../../../models/views/admin';
+import {
+ AdminChildView,
+ AdminViewState,
+ createAdminUrl,
+} from '../../../models/views/admin';
import {createGroupUrl} from '../../../models/views/group';
import {whenVisible} from '../../../utils/dom-util';
import {modalStyles} from '../../../styles/gr-modal-styles';
@@ -29,8 +33,6 @@
@customElement('gr-admin-group-list')
export class GrAdminGroupList extends LitElement {
- readonly path = '/admin/groups';
-
@query('#createModal') private createModal?: HTMLDialogElement;
@query('#createNewModal') private createNewModal?: GrCreateGroupDialog;
@@ -87,7 +89,7 @@
.itemsPerPage=${this.groupsPerPage}
.loading=${this.loading}
.offset=${this.offset}
- .path=${this.path}
+ .path=${createAdminUrl({adminView: AdminChildView.GROUPS})}
@create-clicked=${() => this.handleCreateClicked()}
>
<table id="list" class="genericList">
@@ -167,18 +169,14 @@
*
* private but used in test
*/
- maybeOpenCreateModal(params?: AdminViewState) {
+ async maybeOpenCreateModal(params?: AdminViewState) {
if (params?.openCreateModal) {
- assertIsDefined(this.createModal, 'createModal');
- this.createModal.showModal();
+ await this.updateComplete;
+ if (!this.createModal?.open) this.createModal?.showModal();
}
}
- /**
- * Generates groups link (/admin/groups/<uuid>)
- *
- * private but used in test
- */
+ // private but used in test
computeGroupUrl(encodedId: string) {
const groupId = decodeURIComponent(encodedId) as GroupId;
return createGroupUrl({groupId});
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.ts b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.ts
index 1b128aa..e9b7ea0 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list_test.ts
@@ -120,17 +120,17 @@
assert.equal(element.groups.slice(0, SHOWN_ITEMS_COUNT).length, 25);
});
- test('maybeOpenCreateModal', () => {
+ test('maybeOpenCreateModal', async () => {
const modalOpen = sinon.stub(
queryAndAssert<HTMLDialogElement>(element, '#createModal'),
'showModal'
);
- element.maybeOpenCreateModal();
+ await element.maybeOpenCreateModal();
assert.isFalse(modalOpen.called);
- element.maybeOpenCreateModal(undefined);
+ await element.maybeOpenCreateModal(undefined);
assert.isFalse(modalOpen.called);
value.openCreateModal = true;
- element.maybeOpenCreateModal(value);
+ await element.maybeOpenCreateModal(value);
assert.isTrue(modalOpen.called);
});
});
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
index 4808d00..8d5689c 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
@@ -6,9 +6,8 @@
import '@polymer/iron-input/iron-input';
import '../../../styles/gr-form-styles';
import '../../../styles/shared-styles';
-import {encodeURL, getBaseUrl} from '../../../utils/url-util';
import {page} from '../../../utils/page-wrapper-utils';
-import {GroupName} from '../../../types/common';
+import {GroupId, GroupName} from '../../../types/common';
import {getAppContext} from '../../../services/app-context';
import {formStyles} from '../../../styles/gr-form-styles';
import {sharedStyles} from '../../../styles/shared-styles';
@@ -16,6 +15,7 @@
import {customElement, query, property} from 'lit/decorators.js';
import {BindValueChangeEvent} from '../../../types/events';
import {fireEvent} from '../../../utils/event-util';
+import {createGroupUrl} from '../../../models/views/group';
declare global {
interface HTMLElementTagNameMap {
@@ -75,10 +75,6 @@
fireEvent(this, 'has-new-group-name');
}
- private computeGroupUrl(groupId: string) {
- return getBaseUrl() + '/admin/groups/' + encodeURL(groupId, true);
- }
-
override focus() {
this.input.focus();
}
@@ -89,7 +85,9 @@
if (groupRegistered.status !== 201) return;
return this.restApiService.getGroupConfig(name).then(group => {
if (!group) return;
- page.show(this.computeGroupUrl(String(group.group_id!)));
+ const groupId = String(group.group_id!) as GroupId;
+ // TODO: Use navigation service instead of `page.show()` directly.
+ page.show(createGroupUrl({groupId}));
});
});
}
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
index a90abbd..bb59ccc 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
@@ -7,7 +7,6 @@
import '../../shared/gr-autocomplete/gr-autocomplete';
import '../../shared/gr-button/gr-button';
import '../../shared/gr-select/gr-select';
-import {encodeURL, getBaseUrl} from '../../../utils/url-util';
import {page} from '../../../utils/page-wrapper-utils';
import {
BranchName,
@@ -24,6 +23,7 @@
import {customElement, query, property, state} from 'lit/decorators.js';
import {fireEvent} from '../../../utils/event-util';
import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
+import {createRepoUrl} from '../../../models/views/repo';
declare global {
interface HTMLElementTagNameMap {
@@ -183,10 +183,6 @@
`;
}
- _computeRepoUrl(repoName: string) {
- return getBaseUrl() + '/admin/repos/' + encodeURL(repoName, true);
- }
-
override focus() {
this.input?.focus();
}
@@ -199,7 +195,8 @@
);
if (repoRegistered.status === 201) {
this.repoCreated = true;
- page.show(this._computeRepoUrl(this.repoConfig.name));
+ // TODO: Use navigation service instead of `page.show()` directly.
+ page.show(createRepoUrl({repo: this.repoConfig.name}));
}
return repoRegistered;
}
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
index 389e7c4..52e0b3f 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import '../gr-access-section/gr-access-section';
-import {encodeURL, getBaseUrl, singleDecodeURL} from '../../../utils/url-util';
+import {singleDecodeURL} from '../../../utils/url-util';
import {navigationToken} from '../../core/gr-navigation/gr-navigation';
import {toSortedPermissionsArray} from '../../../utils/access-util';
import {
@@ -44,6 +44,7 @@
import {resolve} from '../../../models/dependency';
import {createChangeUrl} from '../../../models/views/change';
import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
+import {createRepoUrl, RepoDetailView} from '../../../models/views/repo';
const NOTHING_TO_SAVE = 'No changes to save.';
@@ -726,10 +727,10 @@
computeParentHref() {
if (!this.inheritsFrom?.name) return '';
- return `${getBaseUrl()}/admin/repos/${encodeURL(
- this.inheritsFrom.name,
- true
- )},access`;
+ return createRepoUrl({
+ repo: this.inheritsFrom.name,
+ detail: RepoDetailView.ACCESS,
+ });
}
private handleEditInheritFromTextChanged(e: ValueChangedEvent) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
index 3b193a6..7eef7a4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
@@ -442,6 +442,9 @@
}
private getPath(repo?: RepoName, detailType?: RepoDetailView) {
+ // TODO: Replace with `createRepoUrl()`, but be aware that `encodeURL()`
+ // gets `false` as a second parameter here. The router pattern in gr-router
+ // does not handle the filter URLs, if the repo is not encoded!
return `/admin/repos/${encodeURL(repo ?? '', false)},${detailType}`;
}
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.ts
index 28fc751..ec1bb82 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.ts
@@ -2434,6 +2434,18 @@
});
suite('create new', () => {
+ setup(async () => {
+ stubRestApi('getRepoBranches').resolves(createBranchesList(3));
+
+ element.params = {
+ view: GerritView.REPO,
+ repo: 'test' as RepoName,
+ detail: RepoDetailView.BRANCHES,
+ };
+ await element.paramsChanged();
+ await element.updateComplete;
+ });
+
test('handleCreateClicked called when create-click fired', () => {
const handleCreateClickedStub = sinon.stub(
element,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
index c6c6efd..2fd7e79 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
@@ -6,23 +6,23 @@
import '../../shared/gr-dialog/gr-dialog';
import '../../shared/gr-list-view/gr-list-view';
import '../gr-create-repo-dialog/gr-create-repo-dialog';
-import {
- RepoName,
- ProjectInfoWithName,
- WebLinkInfo,
-} from '../../../types/common';
+import {ProjectInfoWithName, WebLinkInfo} from '../../../types/common';
import {GrCreateRepoDialog} from '../gr-create-repo-dialog/gr-create-repo-dialog';
import {RepoState, SHOWN_ITEMS_COUNT} from '../../../constants/constants';
import {fireTitleChange} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
-import {encodeURL, getBaseUrl} from '../../../utils/url-util';
import {tableStyles} from '../../../styles/gr-table-styles';
import {sharedStyles} from '../../../styles/shared-styles';
import {LitElement, PropertyValues, css, html} from 'lit';
import {customElement, property, query, state} from 'lit/decorators.js';
-import {AdminViewState} from '../../../models/views/admin';
+import {
+ AdminChildView,
+ AdminViewState,
+ createAdminUrl,
+} from '../../../models/views/admin';
import {createSearchUrl} from '../../../models/views/search';
import {modalStyles} from '../../../styles/gr-modal-styles';
+import {createRepoUrl} from '../../../models/views/repo';
declare global {
interface HTMLElementTagNameMap {
@@ -32,8 +32,6 @@
@customElement('gr-repo-list')
export class GrRepoList extends LitElement {
- readonly path = '/admin/repos';
-
@query('#createModal') private createModal?: HTMLDialogElement;
@query('#createNewModal') private createNewModal?: GrCreateRepoDialog;
@@ -103,7 +101,7 @@
.items=${this.repos}
.loading=${this.loading}
.offset=${this.offset}
- .path=${this.path}
+ .path=${createAdminUrl({adminView: AdminChildView.REPOS})}
@create-clicked=${() => this.handleCreateClicked()}
>
<table id="list" class="genericList">
@@ -157,11 +155,11 @@
return html`
<tr class="table">
<td class="name">
- <a href=${this.computeRepoUrl(item.name)}>${item.name}</a>
+ <a href=${createRepoUrl({repo: item.name})}>${item.name}</a>
</td>
<td class="repositoryBrowser">${this.renderWebLinks(item)}</td>
<td class="changesLink">
- <a href=${this.computeChangesLink(item.name)}>view all</a>
+ <a href=${createSearchUrl({repo: item.name})}>view all</a>
</td>
<td class="readOnly">
${item.state === RepoState.READ_ONLY ? 'Y' : ''}
@@ -210,14 +208,6 @@
}
}
- private computeRepoUrl(name: string) {
- return `${getBaseUrl()}${this.path}/${encodeURL(name, true)}`;
- }
-
- private computeChangesLink(name: string) {
- return createSearchUrl({repo: name as RepoName});
- }
-
private async getCreateRepoCapability() {
const account = await this.restApiService.getAccount();
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
index 7abfce9..faaee0b 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
@@ -282,12 +282,14 @@
// private but used in test
handleNextPage() {
if (!this.nextArrow || !this.changesPerPage) return;
+ // TODO: Use navigation service instead of `page.show()` directly.
page.show(this.computeNavLink(1));
}
// private but used in test
handlePreviousPage() {
if (!this.prevArrow || !this.changesPerPage) return;
+ // TODO: Use navigation service instead of `page.show()` directly.
page.show(this.computeNavLink(-1));
}
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index 3fff1d4..bcf6937 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -60,6 +60,7 @@
RepoViewState,
} from '../../../models/views/repo';
import {
+ createGroupUrl,
GroupDetailView,
GroupViewModel,
GroupViewState,
@@ -1077,7 +1078,8 @@
}
handleGroupInfoRoute(ctx: PageContext) {
- this.redirect('/admin/groups/' + encodeURIComponent(ctx.params[0]));
+ const groupId = ctx.params[0] as GroupId;
+ this.redirect(createGroupUrl({groupId}));
}
handleGroupSelfRedirectRoute(_: PageContext) {
@@ -1161,6 +1163,8 @@
}
}
+ // TODO: Change the route pattern to match `repo` and `detailView`
+ // separately, and then use `createRepoUrl()` here.
this.redirect(`/admin/repos/${params}`);
}
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index e0933e8..eceb05e 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -73,6 +73,7 @@
import {createDashboardUrl} from '../models/views/dashboard';
import {userModelToken} from '../models/user/user-model';
import {modalStyles} from '../styles/gr-modal-styles';
+import {AdminChildView, createAdminUrl} from '../models/views/admin';
import {ChangeChildView, changeViewModelToken} from '../models/views/change';
interface ErrorInfo {
@@ -215,6 +216,16 @@
createSearchUrl({query: 'is:watched is:open'})
)
);
+ this.shortcuts.addAbstract(Shortcut.GO_TO_REPOS, () =>
+ this.getNavigation().setUrl(
+ createAdminUrl({adminView: AdminChildView.REPOS})
+ )
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_GROUPS, () =>
+ this.getNavigation().setUrl(
+ createAdminUrl({adminView: AdminChildView.GROUPS})
+ )
+ );
subscribe(
this,
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.ts b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.ts
index fd43869..449c041 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.ts
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.ts
@@ -177,9 +177,11 @@
() => {
if (!this.isConnected || !this.path) return;
if (filter) {
+ // TODO: Use navigation service instead of `page.show()` directly.
page.show(`${this.path}/q/filter:${encodeURL(filter, false)}`);
return;
}
+ // TODO: Use navigation service instead of `page.show()` directly.
page.show(this.path);
},
REQUEST_DEBOUNCE_INTERVAL_MS
diff --git a/polygerrit-ui/app/models/views/admin.ts b/polygerrit-ui/app/models/views/admin.ts
index 2ad95a2..3380637 100644
--- a/polygerrit-ui/app/models/views/admin.ts
+++ b/polygerrit-ui/app/models/views/admin.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {GerritView} from '../../services/router/router-model';
+import {getBaseUrl} from '../../utils/url-util';
import {define} from '../dependency';
import {Model} from '../model';
import {ViewState} from './base';
@@ -21,6 +22,17 @@
offset?: number | string;
}
+export function createAdminUrl(state: Omit<AdminViewState, 'view'>) {
+ switch (state.adminView) {
+ case AdminChildView.REPOS:
+ return `${getBaseUrl()}/admin/repos`;
+ case AdminChildView.GROUPS:
+ return `${getBaseUrl()}/admin/groups`;
+ case AdminChildView.PLUGINS:
+ return `${getBaseUrl()}/admin/plugins`;
+ }
+}
+
export const adminViewModelToken = define<AdminViewModel>('admin-view-model');
export class AdminViewModel extends Model<AdminViewState | undefined> {
diff --git a/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts b/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
index da61c41..9ca2213 100644
--- a/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
+++ b/polygerrit-ui/app/services/shortcuts/shortcuts-config.ts
@@ -35,6 +35,8 @@
GO_TO_MERGED_CHANGES = 'GO_TO_MERGED_CHANGES',
GO_TO_ABANDONED_CHANGES = 'GO_TO_ABANDONED_CHANGES',
GO_TO_WATCHED_CHANGES = 'GO_TO_WATCHED_CHANGES',
+ GO_TO_REPOS = 'GO_TO_REPOS',
+ GO_TO_GROUPS = 'GO_TO_GROUPS',
CURSOR_NEXT_CHANGE = 'CURSOR_NEXT_CHANGE',
CURSOR_PREV_CHANGE = 'CURSOR_PREV_CHANGE',
@@ -167,6 +169,16 @@
{key: 'w', combo: ComboKey.G}
);
describe(
+ Shortcut.GO_TO_REPOS,
+ ShortcutSection.EVERYWHERE,
+ 'Go to Repositories',
+ {key: 'r', combo: ComboKey.G}
+ );
+ describe(Shortcut.GO_TO_GROUPS, ShortcutSection.EVERYWHERE, 'Go to Groups', {
+ key: 'g',
+ combo: ComboKey.G,
+ });
+ describe(
Shortcut.TOGGLE_CHECKBOX,
ShortcutSection.ACTIONS,
'Toggle checkbox',
diff --git a/polygerrit-ui/app/utils/admin-nav-util.ts b/polygerrit-ui/app/utils/admin-nav-util.ts
index 6f81d57..7916799 100644
--- a/polygerrit-ui/app/utils/admin-nav-util.ts
+++ b/polygerrit-ui/app/utils/admin-nav-util.ts
@@ -12,7 +12,7 @@
import {hasOwnProperty} from './common-util';
import {GerritView} from '../services/router/router-model';
import {MenuLink} from '../api/admin';
-import {AdminChildView} from '../models/views/admin';
+import {AdminChildView, createAdminUrl} from '../models/views/admin';
import {createGroupUrl, GroupDetailView} from '../models/views/group';
import {createRepoUrl, RepoDetailView} from '../models/views/repo';
@@ -20,7 +20,7 @@
{
name: 'Repositories',
noBaseUrl: true,
- url: '/admin/repos',
+ url: createAdminUrl({adminView: AdminChildView.REPOS}),
view: 'gr-repo-list' as GerritView,
viewableToAll: true,
},
@@ -28,7 +28,7 @@
name: 'Groups',
section: 'Groups',
noBaseUrl: true,
- url: '/admin/groups',
+ url: createAdminUrl({adminView: AdminChildView.GROUPS}),
view: 'gr-admin-group-list' as GerritView,
},
{
@@ -36,7 +36,7 @@
capability: 'viewPlugins',
section: 'Plugins',
noBaseUrl: true,
- url: '/admin/plugins',
+ url: createAdminUrl({adminView: AdminChildView.PLUGINS}),
view: 'gr-plugin-list' as GerritView,
},
];