Chromium Code Reviews| Index: appengine/swarming/elements/res/imp/botlist/bot-list.html |
| diff --git a/appengine/swarming/elements/res/imp/botlist/bot-list.html b/appengine/swarming/elements/res/imp/botlist/bot-list.html |
| index 4fcdd08db2557649ef0f6683855efd9cc540d0c4..1cf1c9bb1ed687a1a05d84d37f4c109be3dec0a6 100644 |
| --- a/appengine/swarming/elements/res/imp/botlist/bot-list.html |
| +++ b/appengine/swarming/elements/res/imp/botlist/bot-list.html |
| @@ -26,6 +26,7 @@ |
| <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html"> |
| <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| +<link rel="import" href="/res/imp/common/dynamic-table.html"> |
| <link rel="import" href="/res/imp/common/sort-toggle.html"> |
| <link rel="import" href="/res/imp/common/swarming-app.html"> |
| <link rel="import" href="/res/imp/common/url-param.html"> |
| @@ -37,41 +38,17 @@ |
| <dom-module id="bot-list"> |
| <template> |
| - <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style"> |
| + <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style dynamic-table-style"> |
| bot-filters, bot-list-summary { |
| margin-bottom: 8px; |
| margin-right: 10px; |
| } |
| - .bot { |
| - margin:5px; |
| - max-width:400px; |
| - min-height:100px; |
| - min-width:300px; |
| - } |
| - table { |
| - border-collapse: collapse; |
| - margin-left: 5px; |
| - } |
| - td, th { |
| - border: 1px solid #DDD; |
| - padding: 5px; |
| - } |
| - |
| .quarantined, .bad-device { |
| background-color: #ffdddd; |
| } |
| .dead { |
| background-color: #cccccc; |
| } |
| - |
| - th { |
| - position: relative; |
| - } |
| - sort-toggle { |
| - position: absolute; |
| - right: 0; |
| - top: 0.4em; |
| - } |
| .bot-list th > span { |
| /* Leave space for sort-toggle*/ |
| padding-right: 30px; |
| @@ -111,7 +88,7 @@ |
| <bot-list-summary |
| columns="[[_columns]]" |
| fleet="[[_fleet]]" |
| - filtered_bots="[[_filteredSortedBots]]" |
| + filtered_bots="[[_filteredSortedItems]]" |
| sort="[[_sortstr]]" |
| verbose="[[_verbose]]"> |
| </bot-list-summary> |
| @@ -122,7 +99,7 @@ |
| auth_headers="[[_auth_headers]]" |
| query_params="[[_query_params]]" |
| - bots="{{_bots}}" |
| + bots="{{_items}}" |
| busy="{{_busy}}" |
| dimensions="{{_dimensions}}" |
| fleet="{{_fleet}}" |
| @@ -161,7 +138,7 @@ |
| </th> |
| <template is="dom-repeat" |
| - items="[[_plain_columns]]" |
| + items="[[_plainColumns]]" |
| as="c"> |
| <th hidden$="[[_hide(c)]]"> |
| <span>[[_header(c)]]</span> |
| @@ -175,7 +152,7 @@ |
| </thead> |
| <tbody> |
| <template id="bot_table" is="dom-repeat" |
| - items="[[_filteredSortedBots]]" |
| + items="[[_filteredSortedItems]]" |
| as="bot" |
| initial-count=50> |
| @@ -192,7 +169,7 @@ |
| </td> |
| <template is="dom-repeat" |
| - items="[[_plain_columns]]" |
| + items="[[_plainColumns]]" |
| as="c"> |
| <td hidden$="[[_hide(c)]]"> |
| [[_column(c, bot, _verbose)]] |
| @@ -208,7 +185,7 @@ |
| <td></td> |
| <td hidden$="[[_hide('task', _columns.*)]]"></td> |
| <template is="dom-repeat" |
| - items="[[_plain_columns]]" |
| + items="[[_plainColumns]]" |
| as="c"> |
| <td hidden$="[[_hide(c)]]"> |
| [[_deviceColumn(c, device, _verbose)]] |
| @@ -226,11 +203,13 @@ |
| </template> |
| <script> |
| (function(){ |
| - var special_columns = ["id", "task"]; |
| + // see dynamic-table for more information on specialColumns, headerMap, |
| + // columnMap, and specialSort |
| + var specialColumns = ["id", "task"]; |
| var headerMap = { |
| - // "id" and "task" are special, so they don't go here and have their |
| - // headers hard-coded below. |
| + // "id" and "task" are special, so they don't go here. They have their |
| + // headers hard-coded above. |
| "android_devices": "Android Devices", |
| "cores": "Cores", |
| "cpu": "CPU", |
| @@ -245,10 +224,6 @@ |
| "xcode_version": "XCode Version", |
| }; |
| - // This maps column name to a function that will return the content for a |
| - // given bot. These functions are bound to this element, and have access |
| - // to all functions defined here and in bot-list-shared. If a column |
| - // is not listed here, a sane default will be used (see _column()). |
| var columnMap = { |
| android_devices: function(bot) { |
| var devs = this._attribute(bot, "android_devices", "0"); |
| @@ -348,8 +323,6 @@ |
| } |
| } |
| - // specialSort defines any custom sorting rules. By default, a |
| - // naturalCompare of the column content is done. |
| var specialSort = { |
| android_devices: function(dir, botA, botB) { |
| // We sort on the number of attached devices. Note that this |
| @@ -370,7 +343,8 @@ |
| Polymer({ |
| is: 'bot-list', |
| - behaviors: [SwarmingBehaviors.BotListBehavior], |
| + behaviors: [SwarmingBehaviors.BotListBehavior, |
| + SwarmingBehaviors.DynamicTableBehavior], |
| properties: { |
| @@ -378,40 +352,34 @@ |
| type: String, |
| }, |
| - _bots: { |
| - type: Array, |
| - }, |
| - |
| - _columns: { |
| - type: Array, |
| + // for dynamic table |
| + _columnMap: { |
| + type: Object, |
| + value: function() { |
| + return columnMap; |
| + } |
| }, |
| - |
| - _filter: { |
| - type: Function, |
| + _headerMap: { |
| + type: Object, |
| value: function() { |
| - return true; |
| + return headerMap; |
| }, |
| }, |
| - |
| - _filteredSortedBots: { |
| + // special columns contain html. non-special (i.e. normal colunns) just |
| + // contain text. |
| + _specialColumns: { |
| type: Array, |
| - computed: "_filterAndSort(_bots,_filter.*,_sort.*)" |
| + value: function() { |
|
stephana
2016/08/16 13:28:06
This will always return the same reference to spec
kjlubick
2016/08/16 13:41:25
Good call, will address on next CL.
|
| + return specialColumns; |
| + } |
| }, |
| - |
| - _plain_columns: { |
| - type: Array, |
| - computed: "_stripSpecial(_columns.*)", |
| - }, |
| - |
| - // _sort is an Object {name:String, direction:String}. |
| - _sort: { |
| + _specialSort: { |
| type: Object, |
| - computed: "_makeObject(_sortstr)", |
| + value: function() { |
| + return specialSort; |
| + } |
| }, |
| - _verbose: { |
| - type: Boolean, |
| - } |
| }, |
| _botClass: function(bot) { |
| @@ -430,20 +398,6 @@ |
| }, |
| - _column: function(col, bot) { |
| - var f = columnMap[col]; |
| - if (!f) { |
| - f = function(bot) { |
| - var c = this._attribute(bot, col, "none"); |
| - if (this._verbose) { |
| - return c.join(" | "); |
| - } |
| - return c[0]; |
| - } |
| - } |
| - return f.bind(this)(bot); |
| - }, |
| - |
| _androidAliasDevice: function(device) { |
| if (device.notReady) { |
| return UNAUTHENTICATED.toUpperCase(); |
| @@ -466,84 +420,6 @@ |
| return ""; |
| }, |
| - _filterAndSort: function(a,b,c) { |
| - // We intentionally sort this._bots (and not a copy) to allow users to |
| - // "chain" sorts, that is, sort by one thing and then another, and |
| - // have both orderings properly impact the list. |
| - swarming.stableSort(this._bots, this._sortBotTable.bind(this)); |
| - var bots = this._bots; |
| - if (this._filter) { |
| - bots = bots.filter(this._filter.bind(this)); |
| - } |
| - |
| - return bots; |
| - }, |
| - |
| - _header: function(col){ |
| - return headerMap[col] || col; |
| - }, |
| - |
| - _hide: function(col) { |
| - return this._columns.indexOf(col) === -1; |
| - }, |
| - |
| - _makeObject: function(sortstr){ |
| - if (!sortstr) { |
| - return undefined; |
| - } |
| - var pieces = sortstr.split(":"); |
| - if (pieces.length != 2) { |
| - // fail safe |
| - return {name: "id", direction:"desc"}; |
| - } |
| - return { |
| - name: pieces[0], |
| - direction: pieces[1], |
| - } |
| - }, |
| - |
| - _reRender: function(filter, sort) { |
| - this.$.bot_table.render(); |
| - }, |
| - |
| - _sortBotTable: function(botA, botB) { |
| - if (!this._sort) { |
| - return 0; |
| - } |
| - var dir = 1; |
| - if (this._sort.direction === "desc") { |
| - dir = -1; |
| - } |
| - var sort = specialSort[this._sort.name]; |
| - if (sort) { |
| - return sort.bind(this)(dir, botA, botB); |
| - } |
| - // Default to a natural compare of the columns. |
| - var botACol = this._column(this._sort.name, botA); |
| - var botBCol = this._column(this._sort.name, botB); |
| - |
| - return dir * swarming.naturalCompare(botACol, botBCol); |
| - }, |
| - |
| - _sortChange: function(e) { |
| - // The event we get from sort-toggle tells us the name of what needs |
| - // to be sorting and how to sort it. |
| - if (!(e && e.detail && e.detail.name)) { |
| - return; |
| - } |
| - // should trigger the computation of _sort and __filterAndSort |
| - this.set("_sortstr", e.detail.name +":"+e.detail.direction); |
| - }, |
| - |
| - // _stripSpecial removes the special columns and sorts the remaining |
| - // columns so they always appear in the same order, regardless of |
| - // the order they are added. |
| - _stripSpecial: function(){ |
| - return this._columns.filter(function(c){ |
| - return special_columns.indexOf(c) === -1; |
| - }).sort(); |
| - }, |
| - |
| _taskLink: function(data) { |
| if (data && data.task_id) { |
| return "/user/task/" + data.task_id; |