Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2301)

Unified Diff: appengine/swarming/elements/res/imp/common/dynamic-table-behavior.html

Issue 2269643002: Extract shared filters and aliasing code (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: Address nit Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: appengine/swarming/elements/res/imp/common/dynamic-table-behavior.html
diff --git a/appengine/swarming/elements/res/imp/common/dynamic-table-behavior.html b/appengine/swarming/elements/res/imp/common/dynamic-table-behavior.html
new file mode 100644
index 0000000000000000000000000000000000000000..71c3fef6033cb95f5debc2d4a61ed1dc0379d303
--- /dev/null
+++ b/appengine/swarming/elements/res/imp/common/dynamic-table-behavior.html
@@ -0,0 +1,272 @@
+<!--
+ Copyright 2016 The LUCI Authors. All rights reserved.
+ Use of this source code is governed under the Apache License, Version 2.0
+ that can be found in the LICENSE file.
+
+ This file contains most of the logic needed to create a dynamic table. It is broken up into two
+ parts, a style dom-module called "dynamic-table-style" and a behavior called
+ SwarmingBehaviors.DynamicTableBehavior. This behavior ties together filtering, sorting and column
+ content. It also offers a few utilities to make creating the table easier. A client of these two
+ parts needs to create the templates to actually draw the <table>,<tr> and so on. See
+ bot-list.html and task-list.html for examples.
+
+ A client should use the provided style set as follows:
+
+ <link rel="import" href="/res/imp/common/dynamic-table-behavior.html">
+ ...
+ <template>
+ <style include="dynamic-table-style">
+ ...
+
+ This behavior has already defined the following properties, which a client should bind to:
+ _columns, Array<String>, The columns that should be shown.
+ _items, Array<Object>, Those elements that may be displayed and/or sorted, depending on the
+ settings.
+ _filter, Function, Given an element from _items, return a boolean if the item should be shown.
+ This function will be bound to this element.
+ _sortstr, String, A String representation of the current state of sorting like
+ [name]:["asc", "desc"].
+ _verbose, Boolean, If the verbose contents of the table should be shown.
+
+ A client must define the following properties:
+ _columnMap: Object, a mapping of column name to a function that will return the content for a
+ given bot. These functions are bound to this element. If a column is not listed here, a sane
+ default will be used (see _column()).
+ _headerMap: Object, a mapping of column name to the displayed text for a column.
+ _specialColumns, Array<String> A list of "special" column names, that is, columns which will
+ have html in them, provided by the client. non-special (i.e. plain colunns) just contain
+ text and will have their content provided by _attribute (see below).
+ _specialSort, Object, A mapping of column name to a function that implements custom sorting
+ rules. The function will be given (dir, a, b) and is expected to return an int, as a normal
+ sort comparison function would. Otherwise, natural comparison of a and b is used
+ (see _compare()).
+
+ A client must define the following methods:
+ _attribute(i, col, default): Given the item i, return an array of values for the column "col",
+ or an array containting just the default, if not. This is only used as a default when a
+ column does not appear in _columnMap.
+
+ This behavior provides the following properties:
+ _filteredSortedItems, Array<Object>, The list of items that should shown, after filtering and
+ sorting.
+ _plainColumns, Array<String>, the list of columns with any special columns stripped out.
+
+ This behavior provides the following methods:
+ _column(col, item): Return the text content of item for a column.
+ _header(col): Return the header for a column, defaulting to the column name.
+ _hide(col): Return a boolean based on whether to hide this column.
+ _sortChange(event): Update the sorting based on an event created by sort-toggle.
+ -->
+<link rel="import" href="common-behavior.html">
+<dom-module id="dynamic-table-style">
+ <template>
+ <style>
+ table {
+ border-collapse: collapse;
+ margin-left: 5px;
+ }
+ td, th {
+ border: 1px solid #DDD;
+ padding: 5px;
+ }
+ th {
+ position: relative;
+ }
+ sort-toggle {
+ position: absolute;
+ right: 0;
+ top: 0.4em;
+ }
+ </style>
+
+ </template>
+</dom-module>
+
+<script>
+ (function(){
+ // This behavior wraps up all the shared swarming functionality.
+ SwarmingBehaviors.DynamicTableBehavior = [SwarmingBehaviors.CommonBehavior, {
+
+ properties: {
+
+ _columns: {
+ type: Array,
+ },
+
+ _filter: {
+ type: Function,
+ },
+
+ _filteredSortedItems: {
+ type: Array,
+ computed: "_filterAndSort(_items,_filter.*,_sort.*)"
+ },
+
+ _items: {
+ type: Array,
+ },
+
+ _plainColumns: {
+ type: Array,
+ computed: "_stripSpecial(_columns.*)",
+ },
+
+ // _sort is an Object {name:String, direction:String}.
+ _sort: {
+ type: Object,
+ computed: "_makeSortObject(_sortstr)",
+ },
+
+ _sortstr: {
+ type: String,
+ },
+
+ _verbose: {
+ type: Boolean,
+ }
+ },
+
+ _column: function(col, key) {
+ var f = this._columnMap[col];
+ if (!f) {
+ f = function(key) {
+ var c = this._attribute(key, col, "none");
+ if (this._verbose) {
+ return c.join(" | ");
+ }
+ return c[0];
+ }
+ }
+ return f.bind(this)(key);
+ },
+
+ _compare: function(a, b) {
+ if (!this._sort) {
+ return 0;
+ }
+ var dir = 1;
+ if (this._sort.direction === "desc") {
+ dir = -1;
+ }
+ var sort = this._specialSort[this._sort.name];
+ if (sort) {
+ return sort.bind(this)(dir, a, b);
+ }
+ // Default to a natural compare of the columns.
+ var aCol = this._column(this._sort.name, a);
+ var bCol = this._column(this._sort.name, b);
+
+ return dir * swarming.naturalCompare(aCol, bCol);
+ },
+
+ _filterAndSort: function() {
+ // We intentionally sort this._items (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._items, this._compare.bind(this));
+ var items = this._items;
+ if (this._filter) {
+ items = items.filter(this._filter.bind(this));
+ }
+
+ return items;
+ },
+
+ _header: function(col){
+ return this._headerMap[col] || col;
+ },
+
+ _hide: function(col) {
+ return this._columns.indexOf(col) === -1;
+ },
+
+ _makeSortObject: function(sortstr){
+ if (!sortstr) {
+ return undefined;
+ }
+ var pieces = sortstr.split(":");
+ if (pieces.length != 2) {
+ // fail safe
+ return {name: "id", direction: "asc"};
+ }
+ return {
+ name: pieces[0],
+ direction: pieces[1],
+ }
+ },
+
+ _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 this._specialColumns.indexOf(c) === -1;
+ }.bind(this)).sort();
+ },
+
+ // Common columns shared between tasklist and botlist
+ _commonColumns: function() {
+ // return a fresh object so all elements have their own copy
+ return {
+ android_devices: function(bot) {
+ var devs = this._attribute(bot, "android_devices", "0");
+ if (this._verbose) {
+ return devs.join(" | ") + " devices available";
+ }
+ // max() works on strings as long as they can be coerced to Number.
+ return Math.max(...devs) + " devices available";
+ },
+ device_type: function(bot) {
+ var dt = this._attribute(bot, "device_type", "none");
+ dt = dt[0];
+ var alias = swarming.alias.android(dt);
+ if (alias === "unknown") {
+ return dt;
+ }
+ return swarming.alias.apply(dt, alias);
+ },
+ gpu: function(bot){
+ var gpus = this._attribute(bot, "gpu", "none");
+ var verbose = []
+ var named = [];
+ // non-verbose mode has only the top level GPU info "e.g. NVidia"
+ // which is found by looking for gpu ids w/o a colon.
+ gpus.forEach(function(g){
+ var alias = swarming.alias.gpu(g);
+ if (alias === "unknown") {
+ verbose.push(g);
+ if (g.indexOf(":") === -1) {
+ named.push(g);
+ }
+ return;
+ }
+ verbose.push(swarming.alias.apply(g, alias));
+ if (g.indexOf(":") === -1) {
+ named.push(swarming.alias.apply(g, alias));
+ }
+ }.bind(this))
+ if (this._verbose || !named.length) {
+ return verbose.join(" | ");
+ }
+ return named.join(" | ");
+ },
+ pool: function(bot) {
+ var pool = this._attribute(bot, "pool");
+ return pool.join(" | ");
+ },
+ };
+ },
+
+
+ }];
+ })();
+</script>

Powered by Google App Engine
This is Rietveld 408576698