Add subsection dropdown to gr-admin-view

Bug: Issue 8671
Change-Id: I9f84e6d11477f3a18ea966dd6ded3ca79f9e10ac
This commit is contained in:
Becky Siegel
2018-04-20 10:08:01 +02:00
parent 4c60d8c698
commit e88c3a2d8c
4 changed files with 291 additions and 40 deletions

View File

@@ -24,6 +24,8 @@ limitations under the License.
<link rel="import" href="../../../styles/gr-page-nav-styles.html"> <link rel="import" href="../../../styles/gr-page-nav-styles.html">
<link rel="import" href="../../../styles/shared-styles.html"> <link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html"> <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-dropdown-list/gr-dropdown-list.html">
<link rel="import" href="../../shared/gr-icons/gr-icons.html">
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html"> <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
<link rel="import" href="../../shared/gr-page-nav/gr-page-nav.html"> <link rel="import" href="../../shared/gr-page-nav/gr-page-nav.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
@@ -43,7 +45,38 @@ limitations under the License.
<template> <template>
<style include="shared-styles"></style> <style include="shared-styles"></style>
<style include="gr-menu-page-styles"></style> <style include="gr-menu-page-styles"></style>
<style include="gr-page-nav-styles"></style> <style include="gr-page-nav-styles">
gr-dropdown-list {
--trigger-style: {
text-transform: none;
}
}
.breadcrumbText {
/* Same as dropdown trigger so chevron spacing is consistent. */
padding: 5px 4px;
}
iron-icon {
margin: 0 .2em;
}
.breadcrumb {
align-items: center;
display: flex;
}
.mainHeader {
align-items: baseline;
border-bottom: 1px solid var(--border-color);
display: flex;
}
.selectText {
display: none;
}
.selectText.show {
display: inline-block;
}
main.breadcrumbs:not(.table) {
margin-top: 1em;
}
</style>
<gr-page-nav class="navStyles"> <gr-page-nav class="navStyles">
<ul class="sectionContent"> <ul class="sectionContent">
<template id="adminNav" is="dom-repeat" items="[[_filteredLinks]]"> <template id="adminNav" is="dom-repeat" items="[[_filteredLinks]]">
@@ -75,29 +108,26 @@ limitations under the License.
</template> </template>
</ul> </ul>
</gr-page-nav> </gr-page-nav>
<template is="dom-if" if="[[_subsectionLinks.length]]">
<section class="mainHeader">
<span class="breadcrumb">
<span class="breadcrumbText">[[_breadcrumbParentName]]</span>
<iron-icon icon="gr-icons:chevron-right"></iron-icon>
</span>
<gr-dropdown-list
lowercase
id="pageSelect"
value="[[_computeSelectValue(params)]]"
items="[[_subsectionLinks]]"
on-value-change="_handleSubsectionChange">
</gr-dropdown-list>
</section>
</template>
<template is="dom-if" if="[[_showRepoList]]" restamp="true"> <template is="dom-if" if="[[_showRepoList]]" restamp="true">
<main class="table"> <main class="table">
<gr-repo-list class="table" params="[[params]]"></gr-repo-list> <gr-repo-list class="table" params="[[params]]"></gr-repo-list>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showRepoMain]]" restamp="true">
<main>
<gr-repo repo="[[params.repo]]"></gr-repo>
</main>
</template>
<template is="dom-if" if="[[_showGroup]]" restamp="true">
<main>
<gr-group
group-id="[[params.groupId]]"
on-name-changed="_updateGroupName"></gr-group>
</main>
</template>
<template is="dom-if" if="[[_showGroupMembers]]" restamp="true">
<main>
<gr-group-members
group-id="[[params.groupId]]"></gr-group-members>
</main>
</template>
<template is="dom-if" if="[[_showGroupList]]" restamp="true"> <template is="dom-if" if="[[_showGroupList]]" restamp="true">
<main class="table"> <main class="table">
<gr-admin-group-list class="table" params="[[params]]"> <gr-admin-group-list class="table" params="[[params]]">
@@ -109,35 +139,53 @@ limitations under the License.
<gr-plugin-list class="table" params="[[params]]"></gr-plugin-list> <gr-plugin-list class="table" params="[[params]]"></gr-plugin-list>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showRepoMain]]" restamp="true">
<main class="breadcrumbs">
<gr-repo repo="[[params.repo]]"></gr-repo>
</main>
</template>
<template is="dom-if" if="[[_showGroup]]" restamp="true">
<main class="breadcrumbs">
<gr-group
group-id="[[params.groupId]]"
on-name-changed="_updateGroupName"></gr-group>
</main>
</template>
<template is="dom-if" if="[[_showGroupMembers]]" restamp="true">
<main class="breadcrumbs">
<gr-group-members
group-id="[[params.groupId]]"></gr-group-members>
</main>
</template>
<template is="dom-if" if="[[_showRepoDetailList]]" restamp="true"> <template is="dom-if" if="[[_showRepoDetailList]]" restamp="true">
<main class="table"> <main class="table breadcrumbs">
<gr-repo-detail-list <gr-repo-detail-list
params="[[params]]" params="[[params]]"
class="table"></gr-repo-detail-list> class="table"></gr-repo-detail-list>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showGroupAuditLog]]" restamp="true"> <template is="dom-if" if="[[_showGroupAuditLog]]" restamp="true">
<main class="table"> <main class="table breadcrumbs">
<gr-group-audit-log <gr-group-audit-log
group-id="[[params.groupId]]" group-id="[[params.groupId]]"
class="table"></gr-group-audit-log> class="table"></gr-group-audit-log>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showRepoCommands]]" restamp="true"> <template is="dom-if" if="[[_showRepoCommands]]" restamp="true">
<main> <main class="breadcrumbs">
<gr-repo-commands <gr-repo-commands
repo="[[params.repo]]"></gr-repo-commands> repo="[[params.repo]]"></gr-repo-commands>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showRepoAccess]]" restamp="true"> <template is="dom-if" if="[[_showRepoAccess]]" restamp="true">
<main class="table"> <main class="breadcrumbs">
<gr-repo-access <gr-repo-access
path="[[path]]" path="[[path]]"
repo="[[params.repo]]"></gr-repo-access> repo="[[params.repo]]"></gr-repo-access>
</main> </main>
</template> </template>
<template is="dom-if" if="[[_showRepoDashboards]]" restamp="true"> <template is="dom-if" if="[[_showRepoDashboards]]" restamp="true">
<main class="table"> <main class="table breadcrumbs">
<gr-repo-dashboards repo="[[params.repo]]"></gr-repo-dashboards> <gr-repo-dashboards repo="[[params.repo]]"></gr-repo-dashboards>
</main> </main>
</template> </template>

View File

@@ -29,6 +29,7 @@
path: String, path: String,
adminView: String, adminView: String,
_breadcrumbParentName: String,
_repoName: String, _repoName: String,
_groupId: { _groupId: {
type: Number, type: Number,
@@ -103,6 +104,8 @@
options) options)
.then(res => { .then(res => {
this._filteredLinks = res.links; this._filteredLinks = res.links;
this._breadcrumbParentName = res.expandedSection ?
res.expandedSection.name : '';
if (!res.expandedSection) { if (!res.expandedSection) {
this._subsectionLinks = []; this._subsectionLinks = [];
@@ -112,7 +115,7 @@
.concat(res.expandedSection.children).map(section => { .concat(res.expandedSection.children).map(section => {
return { return {
text: !section.detailType ? 'Home' : section.name, text: !section.detailType ? 'Home' : section.name,
value: section.view + section.detailType, value: section.view + (section.detailType || ''),
view: section.view, view: section.view,
url: section.url, url: section.url,
detailType: section.detailType, detailType: section.detailType,
@@ -123,6 +126,17 @@
}); });
}, },
_computeSelectValue(params) {
if (!params || !params.view) { return; }
return params.view + (params.detail || '');
},
_selectedIsCurrentPage(selected) {
return (selected.parent === (this._repoName || this._groupId) &&
selected.view === this.params.view &&
selected.detailType === this.params.detail);
},
_handleSubsectionChange(e) { _handleSubsectionChange(e) {
const selected = this._subsectionLinks const selected = this._subsectionLinks
.find(section => section.value === e.detail.value); .find(section => section.value === e.detail.value);
@@ -165,16 +179,22 @@
this.set('_showPluginList', isAdminView && this.set('_showPluginList', isAdminView &&
params.adminView === 'gr-plugin-list'); params.adminView === 'gr-plugin-list');
let needsReload = false;
if (params.repo !== this._repoName) { if (params.repo !== this._repoName) {
this._repoName = params.repo || ''; this._repoName = params.repo || '';
// Reloads the admin menu. // Reloads the admin menu.
this.reload(); needsReload = true;
} }
if (params.groupId !== this._groupId) { if (params.groupId !== this._groupId) {
this._groupId = params.groupId || ''; this._groupId = params.groupId || '';
// Reloads the admin menu. // Reloads the admin menu.
this.reload(); needsReload = true;
} }
if (this._breadcrumbParentName && !params.groupId && !params.repo) {
needsReload = true;
}
if (!needsReload) { return; }
this.reload();
}, },
_computeSelectedTitle(params) { _computeSelectedTitle(params) {

View File

@@ -79,7 +79,6 @@ limitations under the License.
name: 'Repositories', name: 'Repositories',
url: '/admin/repos', url: '/admin/repos',
view: 'gr-repo-list', view: 'gr-repo-list',
children: [],
}]; }];
element.params = { element.params = {
@@ -189,17 +188,10 @@ limitations under the License.
}); });
element.reload().then(() => { element.reload().then(() => {
flushAsynchronousOperations(); flushAsynchronousOperations();
assert.equal(element._filteredLinks.length, 3); assert.equal(Polymer.dom(element.root)
.querySelectorAll('.sectionTitle').length, 3);
// Repos assert.equal(element.$$('.breadcrumbText').innerText, 'Test Repo');
assert.equal(element._filteredLinks[0].subsection.children.length, 5); assert.equal(element.$$('#pageSelect').items.length, 6);
assert.equal(element._filteredLinks[0].subsection.name, 'Test Repo');
// Groups
assert.isNotOk(element._filteredLinks[1].subsection);
// Plugins
assert.isNotOk(element._filteredLinks[2].subsection);
done(); done();
}); });
}); });
@@ -287,6 +279,188 @@ limitations under the License.
element.$$('gr-group').fire('name-changed', {name: newName}); element.$$('gr-group').fire('name-changed', {name: newName});
}); });
test('dropdown displays if there is a subsection', () => {
assert.isNotOk(element.$$('.mainHeader'));
element._subsectionLinks = [
{
text: 'Home',
value: 'repo',
view: 'repo',
url: '',
parent: 'my-repo',
detailType: undefined,
},
];
flushAsynchronousOperations();
assert.isOk(element.$$('.mainHeader'));
element._subsectionLinks = undefined;
flushAsynchronousOperations();
assert.equal(getComputedStyle(element.$$('.mainHeader')).display, 'none');
});
test('Dropdown only triggers navigation on explicit select', done => {
element._repoName = 'my-repo';
element.params = {
repo: 'my-repo',
view: Gerrit.Nav.View.REPO,
detail: Gerrit.Nav.RepoDetailView.ACCESS,
};
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
return Promise.resolve({
createGroup: true,
createProject: true,
viewPlugins: true,
});
});
sandbox.stub(element.$.restAPI, 'getAccount', () => {
return Promise.resolve({_id: 1});
});
flushAsynchronousOperations();
const expectedFilteredLinks = [
{
name: 'Repositories',
noBaseUrl: true,
url: '/admin/repos',
view: 'gr-repo-list',
viewableToAll: true,
subsection: {
name: 'my-repo',
view: 'repo',
url: '',
children: [
{
name: 'Access',
view: 'repo',
detailType: 'access',
url: '',
},
{
name: 'Commands',
view: 'repo',
detailType: 'commands',
url: '',
},
{
name: 'Branches',
view: 'repo',
detailType: 'branches',
url: '',
},
{
name: 'Tags',
view: 'repo',
detailType: 'tags',
url: '',
},
{
name: 'Dashboards',
view: 'repo',
detailType: 'dashboards',
url: '',
},
],
},
},
{
name: 'Groups',
section: 'Groups',
noBaseUrl: true,
url: '/admin/groups',
view: 'gr-admin-group-list',
},
{
name: 'Plugins',
capability: 'viewPlugins',
section: 'Plugins',
noBaseUrl: true,
url: '/admin/plugins',
view: 'gr-plugin-list',
},
];
const expectedSubsectionLinks = [
{
text: 'Home',
value: 'repo',
view: 'repo',
url: '',
parent: 'my-repo',
detailType: undefined,
},
{
text: 'Access',
value: 'repoaccess',
view: 'repo',
url: '',
detailType: 'access',
parent: 'my-repo',
},
{
text: 'Commands',
value: 'repocommands',
view: 'repo',
url: '',
detailType: 'commands',
parent: 'my-repo',
},
{
text: 'Branches',
value: 'repobranches',
view: 'repo',
url: '',
detailType: 'branches',
parent: 'my-repo',
},
{
text: 'Tags',
value: 'repotags',
view: 'repo',
url: '',
detailType: 'tags',
parent: 'my-repo',
},
{
text: 'Dashboards',
value: 'repodashboards',
view: 'repo',
url: '',
detailType: 'dashboards',
parent: 'my-repo',
},
];
sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl');
sandbox.spy(element, '_selectedIsCurrentPage');
sandbox.spy(element, '_handleSubsectionChange');
element.reload().then(() => {
assert.deepEqual(element._filteredLinks, expectedFilteredLinks);
assert.deepEqual(element._subsectionLinks, expectedSubsectionLinks);
assert.equal(element.$$('#pageSelect').value, 'repoaccess');
assert.isTrue(element._selectedIsCurrentPage.calledOnce);
// Doesn't trigger navigation from the page select menu.
assert.isFalse(Gerrit.Nav.navigateToRelativeUrl.called);
// When explicitly changed, navigation is called
element.$$('#pageSelect').value = 'repo';
assert.isTrue(element._selectedIsCurrentPage.calledTwice);
assert.isTrue(Gerrit.Nav.navigateToRelativeUrl.calledOnce);
done();
});
});
test('_selectedIsCurrentPage', () => {
element._repoName = 'my-repo';
element.params = {view: 'repo', repo: 'my-repo'};
const selected = {
view: 'repo',
detailType: undefined,
parent: 'my-repo',
};
assert.isTrue(element._selectedIsCurrentPage(selected));
selected.parent = 'my-second-repo';
assert.isFalse(element._selectedIsCurrentPage(selected));
selected.detailType = 'detailType';
assert.isFalse(element._selectedIsCurrentPage(selected));
});
suite('_computeSelectedClass', () => { suite('_computeSelectedClass', () => {
setup(() => { setup(() => {
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => { sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {

View File

@@ -25,7 +25,12 @@ limitations under the License.
margin: 2em auto; margin: 2em auto;
max-width: 50em; max-width: 50em;
} }
main.table { .mainHeader {
margin-left: 14em;
padding: 1em 0 1em 2em;
}
main.table,
.mainHeader {
margin-top: 0; margin-top: 0;
margin-right: 0; margin-right: 0;
margin-left: 14em; margin-left: 14em;
@@ -57,6 +62,10 @@ limitations under the License.
main.table { main.table {
margin: 0; margin: 0;
} }
.mainHeader {
margin-left: 0;
padding: .5em 0 .5em 1em;
}
} }
</style> </style>
</template> </template>