Introduce gr-dropdown element
Creates a new element based on the gr-account-dropdown that can also be used to replace the css-only version in the main header. gr-dropdown accepts two arrays as input-- "items" for links and "topContent" for text to display above the links. TopContent was needed for the account dropdown use case, as it displays name and email address. It also accepts a horizontal-align parameter. gr-account-dropdown is refactored to use the generalized element. Bug: Issue 4666 Change-Id: I65905b81504d1daa46e78312f576bf7482a4577c
This commit is contained in:
@@ -17,21 +17,12 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
|
<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
|
||||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||||
|
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.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">
|
||||||
|
|
||||||
<dom-module id="gr-account-dropdown">
|
<dom-module id="gr-account-dropdown">
|
||||||
<template>
|
<template>
|
||||||
<style>
|
<style>
|
||||||
:host {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.dropdown-trigger {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.dropdown-content {
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 1px 5px rgba(0, 0, 0, .3);
|
|
||||||
}
|
|
||||||
button {
|
button {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -43,51 +34,15 @@ limitations under the License.
|
|||||||
width: 2em;
|
width: 2em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
ul .accountName {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
li .accountInfo,
|
|
||||||
li a {
|
|
||||||
display: block;
|
|
||||||
padding: .85em 1em;
|
|
||||||
}
|
|
||||||
li a:link,
|
|
||||||
li a:visited {
|
|
||||||
color: #00e;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
li a:hover {
|
|
||||||
background-color: #6B82D6;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<gr-button link class="dropdown-trigger" id="trigger"
|
<gr-dropdown items=[[links]] top-content=[[topContent]]
|
||||||
on-tap="_showDropdownTapHandler">
|
|
||||||
<span hidden$="[[_hasAvatars]]" hidden>[[account.name]]</span>
|
|
||||||
<gr-avatar account="[[account]]" hidden$="[[!_hasAvatars]]" hidden
|
|
||||||
image-size="56"></gr-avatar>
|
|
||||||
</gr-button>
|
|
||||||
<iron-dropdown id="dropdown"
|
|
||||||
vertical-align="top"
|
|
||||||
vertical-offset="25"
|
|
||||||
horizontal-align="right">
|
horizontal-align="right">
|
||||||
<div class="dropdown-content">
|
<gr-button link class="dropdown-trigger" id="trigger">
|
||||||
<ul>
|
<span hidden$="[[_hasAvatars]]" hidden>[[account.name]]</span>
|
||||||
<li>
|
<gr-avatar account="[[account]]" hidden$="[[!_hasAvatars]]" hidden
|
||||||
<div class="accountInfo">
|
image-size="56"></gr-avatar>
|
||||||
<div class="accountName">[[account.name]]</div>
|
</gr-button>
|
||||||
<div>[[account.email]]</div>
|
</gr-dropdown>
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a href$="[[_computeRelativeURL('/settings')]]">Settings</a></li>
|
|
||||||
<li><a href$="[[_computeRelativeURL('/switch-account')]]">Switch account</a></li>
|
|
||||||
<li><a href$="[[_computeRelativeURL('/logout')]]">Sign out</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</iron-dropdown>
|
|
||||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
</template>
|
</template>
|
||||||
<script src="gr-account-dropdown.js"></script>
|
<script src="gr-account-dropdown.js"></script>
|
||||||
|
|||||||
@@ -20,26 +20,31 @@
|
|||||||
properties: {
|
properties: {
|
||||||
account: Object,
|
account: Object,
|
||||||
_hasAvatars: Boolean,
|
_hasAvatars: Boolean,
|
||||||
|
links: {
|
||||||
|
type: Array,
|
||||||
|
value: [
|
||||||
|
{name: 'Settings', url: '/settings'},
|
||||||
|
{name: 'Switch account', url: '/switch-account'},
|
||||||
|
{name: 'Sign out', url: '/logout'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
topContent: {
|
||||||
|
type: Array,
|
||||||
|
computed: '_getTopContent(account)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
attached: function() {
|
attached: function() {
|
||||||
this.$.restAPI.getConfig().then(function(cfg) {
|
this.$.restAPI.getConfig().then(function(cfg) {
|
||||||
this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
|
this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.listen(this.$.dropdown, 'tap', '_handleDropdownTap');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleDropdownTap: function(e) {
|
_getTopContent: function(account) {
|
||||||
this.$.dropdown.close();
|
return [
|
||||||
},
|
{text: account.name, bold: true},
|
||||||
|
{text: account.email},
|
||||||
_showDropdownTapHandler: function(e) {
|
];
|
||||||
this.$.dropdown.open();
|
|
||||||
},
|
|
||||||
|
|
||||||
_computeRelativeURL: function(path) {
|
|
||||||
return '//' + window.location.host + path;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -41,11 +41,10 @@ limitations under the License.
|
|||||||
element = fixture('basic');
|
element = fixture('basic');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('tap on trigger opens menu', function() {
|
test('account information', function() {
|
||||||
assert.isFalse(element.$.dropdown.opened);
|
element.account = {name: 'John Doe', email: 'john@doe.com'};
|
||||||
MockInteractions.tap(element.$.trigger);
|
assert.deepEqual(element.topContent,
|
||||||
assert.isTrue(element.$.dropdown.opened);
|
[{text: 'John Doe', bold: true}, {text: 'john@doe.com'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
112
polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
Normal file
112
polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.html
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../../../bower_components/iron-dropdown/iron-dropdown.html">
|
||||||
|
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||||
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
|
<dom-module id="gr-dropdown">
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.dropdown-trigger {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.dropdown-content {
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 1px 5px rgba(0, 0, 0, .3);
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font: inherit;
|
||||||
|
padding: .3em 0;
|
||||||
|
}
|
||||||
|
gr-avatar {
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
ul .accountName {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
li .accountInfo,
|
||||||
|
li a {
|
||||||
|
display: block;
|
||||||
|
padding: .85em 1em;
|
||||||
|
}
|
||||||
|
li a:link,
|
||||||
|
li a:visited {
|
||||||
|
color: #00e;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
li a:hover {
|
||||||
|
background-color: #6B82D6;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.topContent {
|
||||||
|
display: block;
|
||||||
|
padding: .85em 1em;
|
||||||
|
}
|
||||||
|
.bold-text {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<gr-button link class="dropdown-trigger" id="trigger"
|
||||||
|
on-tap="_showDropdownTapHandler">
|
||||||
|
<content></content>
|
||||||
|
</gr-button>
|
||||||
|
<iron-dropdown id="dropdown"
|
||||||
|
vertical-align="top"
|
||||||
|
vertical-offset="25"
|
||||||
|
horizontal-align="[[horizontalAlign]]"
|
||||||
|
on-tap="_handleDropdownTap">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<ul>
|
||||||
|
<template is="dom-if" if="[[topContent]]">
|
||||||
|
<div class="topContent">
|
||||||
|
<template
|
||||||
|
is="dom-repeat"
|
||||||
|
items="[[topContent]]"
|
||||||
|
as="item"
|
||||||
|
initial-count="75">
|
||||||
|
<div class$="[[_getClassIfBold(item.bold)]] top-item">
|
||||||
|
[[item.text]]
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
is="dom-repeat"
|
||||||
|
items="[[items]]"
|
||||||
|
as="link"
|
||||||
|
initial-count="75">
|
||||||
|
<li><a href$="[[_computeRelativeURL(link.url)]]">[[link.name]]</a>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</iron-dropdown>
|
||||||
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
|
</template>
|
||||||
|
<script src="gr-dropdown.js"></script>
|
||||||
|
</dom-module>
|
||||||
57
polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
Normal file
57
polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// 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.
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'gr-dropdown',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
items: Array,
|
||||||
|
topContent: Object,
|
||||||
|
horizontalAlign: {
|
||||||
|
type: String,
|
||||||
|
value: 'left',
|
||||||
|
},
|
||||||
|
_hasAvatars: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
this.$.restAPI.getConfig().then(function(cfg) {
|
||||||
|
this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleDropdownTap: function(e) {
|
||||||
|
this.$.dropdown.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showDropdownTapHandler: function(e) {
|
||||||
|
this.$.dropdown.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
_getClassIfBold: function(bold) {
|
||||||
|
return bold ? 'bold-text' : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeURLHelper: function(host, path) {
|
||||||
|
return '//' + host + path;
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeRelativeURL: function(path) {
|
||||||
|
var host = window.location.host;
|
||||||
|
return this._computeURLHelper(host, path);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
|
<title>gr-dropdown</title>
|
||||||
|
|
||||||
|
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
|
||||||
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
|
||||||
|
<link rel="import" href="gr-dropdown.html">
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-dropdown></gr-dropdown>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-dropdown tests', function() {
|
||||||
|
var element;
|
||||||
|
|
||||||
|
setup(function() {
|
||||||
|
stub('gr-rest-api-interface', {
|
||||||
|
getConfig: function() { return Promise.resolve({}); },
|
||||||
|
});
|
||||||
|
element = fixture('basic');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tap on trigger opens menu', function() {
|
||||||
|
assert.isFalse(element.$.dropdown.opened);
|
||||||
|
MockInteractions.tap(element.$.trigger);
|
||||||
|
assert.isTrue(element.$.dropdown.opened);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_computeRelativeURL', function() {
|
||||||
|
var path = '/test';
|
||||||
|
var host = 'http://www.testsite.com';
|
||||||
|
var computedPath = element._computeURLHelper(host, path);
|
||||||
|
assert.equal(computedPath, '//http://www.testsite.com/test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_getClassIfBold', function() {
|
||||||
|
var bold = true;
|
||||||
|
assert.equal(element._getClassIfBold(bold), 'bold-text');
|
||||||
|
|
||||||
|
bold = false;
|
||||||
|
assert.equal(element._getClassIfBold(bold), '');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Top text exists and is bolded correctly', function() {
|
||||||
|
element.topContent = [{text: 'User', bold: true}, {text: 'email'}];
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
var topItems = Polymer.dom(element.root).querySelectorAll('.top-item');
|
||||||
|
assert.equal(topItems.length, 2);
|
||||||
|
assert.isTrue(topItems[0].classList.contains('bold-text'));
|
||||||
|
assert.isFalse(topItems[1].classList.contains('bold-text'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user