ONOS-1842 - GUI -- Tables have better resizing behavior -- column headers stay a…
…bove their columns and table cell text is constrained to one size. Change-Id: I89ca7d25d46d895c78c41b8250ce40408fbaba85
Showing
21 changed files
with
462 additions
and
383 deletions
... | @@ -16,68 +16,80 @@ | ... | @@ -16,68 +16,80 @@ |
16 | 16 | ||
17 | /* ------ for summary-list tables ------ */ | 17 | /* ------ for summary-list tables ------ */ |
18 | 18 | ||
19 | -table.summary-list { | 19 | +div.summary-list { |
20 | - margin: 0 20px 16px 12px; | 20 | + margin: 0 20px 16px 10px; |
21 | font-size: 10pt; | 21 | font-size: 10pt; |
22 | border-spacing: 0; | 22 | border-spacing: 0; |
23 | } | 23 | } |
24 | 24 | ||
25 | -table.summary-list td.nodata { | 25 | +div.summary-list table { |
26 | + border-collapse: collapse; | ||
27 | + table-layout: fixed; | ||
28 | + empty-cells: show; | ||
29 | + margin: 0; | ||
30 | +} | ||
31 | + | ||
32 | +div.summary-list div.table-body { | ||
33 | + overflow-y: scroll; | ||
34 | +} | ||
35 | + | ||
36 | +div.summary-list tr.no-data td { | ||
26 | text-align: center; | 37 | text-align: center; |
27 | font-style: italic; | 38 | font-style: italic; |
28 | } | 39 | } |
29 | 40 | ||
30 | -.light table.summary-list tr:nth-child(even) { | 41 | +.light div.summary-list tr:nth-child(even) { |
31 | background-color: #ddd; | 42 | background-color: #ddd; |
32 | } | 43 | } |
33 | -.light table.summary-list tr:nth-child(odd) { | 44 | +.light div.summary-list tr:nth-child(odd) { |
34 | background-color: #eee; | 45 | background-color: #eee; |
35 | } | 46 | } |
36 | -.dark table.summary-list tr:nth-child(even) { | 47 | +.dark div.summary-list tr:nth-child(even) { |
37 | background-color: #333; | 48 | background-color: #333; |
38 | } | 49 | } |
39 | -.dark table.summary-list tr:nth-child(odd) { | 50 | +.dark div.summary-list tr:nth-child(odd) { |
40 | background-color: #444; | 51 | background-color: #444; |
41 | } | 52 | } |
42 | 53 | ||
43 | -.light table.summary-list tr.selected { | 54 | +.light div.summary-list tr.selected { |
44 | background-color: deepskyblue; | 55 | background-color: deepskyblue; |
45 | } | 56 | } |
46 | 57 | ||
47 | -.dark table.summary-list tr.selected { | 58 | +.dark div.summary-list tr.selected { |
48 | background-color: #304860; | 59 | background-color: #304860; |
49 | } | 60 | } |
50 | 61 | ||
51 | -table.summary-list td, | 62 | +div.summary-list td { |
52 | -table.summary-list th { | ||
53 | padding: 6px; | 63 | padding: 6px; |
54 | text-align: left; | 64 | text-align: left; |
65 | + word-wrap: break-word; | ||
55 | } | 66 | } |
56 | 67 | ||
57 | -table.summary-list th { | 68 | +div.summary-list .table-header td { |
58 | letter-spacing: 0.02em; | 69 | letter-spacing: 0.02em; |
59 | cursor: pointer; | 70 | cursor: pointer; |
71 | + font-weight: bold; | ||
60 | } | 72 | } |
61 | -table.summary-list th:first-child { | 73 | +div.summary-list .table-header td:first-child { |
62 | border-radius: 8px 0 0 0; | 74 | border-radius: 8px 0 0 0; |
63 | } | 75 | } |
64 | -table.summary-list th:last-child { | 76 | +div.summary-list .table-header td:last-child { |
65 | border-radius: 0 8px 0 0; | 77 | border-radius: 0 8px 0 0; |
66 | } | 78 | } |
67 | 79 | ||
68 | -.light table.summary-list th { | 80 | +.light div.summary-list .table-header td { |
69 | background-color: #bbb; | 81 | background-color: #bbb; |
70 | } | 82 | } |
71 | -.dark table.summary-list th { | 83 | +.dark div.summary-list .table-header td { |
72 | background-color: #222; | 84 | background-color: #222; |
73 | color: #ccc; | 85 | color: #ccc; |
74 | } | 86 | } |
75 | 87 | ||
76 | /* rows are selectable */ | 88 | /* rows are selectable */ |
77 | -table.summary-list td { | 89 | +div.summary-list .table-body td { |
78 | cursor: pointer; | 90 | cursor: pointer; |
79 | } | 91 | } |
80 | 92 | ||
81 | -.dark table.summary-list td { | 93 | +.dark div.summary-list td { |
82 | color: #ccc; | 94 | color: #ccc; |
83 | } | 95 | } | ... | ... |
... | @@ -37,86 +37,68 @@ | ... | @@ -37,86 +37,68 @@ |
37 | prevCol = {}, | 37 | prevCol = {}, |
38 | sortIconAPI; | 38 | sortIconAPI; |
39 | 39 | ||
40 | - // Functions for creating a fixed header on a table (Angular Directive) | 40 | + // Functions for creating a scrolling table body with fixed table header |
41 | 41 | ||
42 | - function setElemWidth(elem, size) { | 42 | + function _width(elem, width) { |
43 | - elem.style('width', size + 'px') | 43 | + elem.style('width', width); |
44 | } | 44 | } |
45 | 45 | ||
46 | - function setColWidth(th, td, size) { | 46 | + function defaultSize(table, width) { |
47 | - setElemWidth(th, size); | 47 | + var thead = table.select('.table-header').select('table'), |
48 | - setElemWidth(td, size); | 48 | + tbody = table.select('.table-body').select('table'), |
49 | + wpx = width + 'px'; | ||
50 | + _width(thead, wpx); | ||
51 | + _width(tbody, wpx); | ||
49 | } | 52 | } |
50 | 53 | ||
51 | - // count number of headers of | 54 | + function adjustTable(table, width, height) { |
52 | - // - assigned width, | 55 | + var thead = table.select('.table-header').select('table'), |
53 | - // - icon width, | 56 | + tbodyDiv = table.select('.table-body'), |
54 | - // - and default width | 57 | + tbody = tbodyDiv.select('table'), |
55 | - // assumes assigned width is not given to icons | 58 | + cstmWidths = {}; |
56 | - // returns the width of all columns that are not icons | ||
57 | - // or have an assigned width | ||
58 | - function getDefaultWidth(headers) { | ||
59 | - var winWidth = fs.windowSize().width, | ||
60 | - iconCols = 0, | ||
61 | - regCols = 0, | ||
62 | - cstmColWidth = 0; | ||
63 | 59 | ||
64 | - headers.each(function (d, i) { | 60 | + function findCstmWidths() { |
65 | - var thElement = d3.select(this), | 61 | + var headers = thead.selectAll('td'); |
66 | - cstmWidth = thElement.attr(colWidth); | ||
67 | 62 | ||
68 | - if (cstmWidth) { | 63 | + headers.each(function (d, i) { |
69 | - cstmColWidth += fs.noPx(cstmWidth); | 64 | + var h = d3.select(this), |
70 | - } else if (thElement.classed(tableIcon)) { | 65 | + index = i.toString(); |
71 | - iconCols += 1; | 66 | + if (h.classed(tableIcon)) { |
72 | - } else { | 67 | + cstmWidths[index] = tableIconTdSize + 'px'; |
73 | - regCols += 1; | 68 | + } |
69 | + if (h.attr(colWidth)) { | ||
70 | + cstmWidths[index] = h.attr(colWidth); | ||
74 | } | 71 | } |
75 | }); | 72 | }); |
76 | - | 73 | + $log.debug('Headers with custom widths: ', cstmWidths); |
77 | - return Math.floor((winWidth - cstmColWidth - | ||
78 | - (iconCols * tableIconTdSize)) / regCols); | ||
79 | } | 74 | } |
80 | 75 | ||
81 | - function setTableWidth(t) { | 76 | + function setTdWidths(elem) { |
82 | - var tHeaders = t.selectAll('th'), | 77 | + var tds = elem.selectAll('tr:not(.ignore-width)').selectAll('td'); |
83 | - defaultColWidth = getDefaultWidth(tHeaders); | 78 | + _width(elem, width + 'px'); |
84 | - | ||
85 | - tHeaders.each(function (d, i) { | ||
86 | - var thElement = d3.select(this), | ||
87 | - tr = t.select('tr:nth-of-type(2)'), | ||
88 | - tdElement = tr.select('td:nth-of-type(' + (i + 1) + ')'), | ||
89 | - custWidth = thElement.attr(colWidth); | ||
90 | 79 | ||
91 | - if (custWidth) { | 80 | + tds.each(function (d, i) { |
92 | - setColWidth(thElement, tdElement, fs.noPx(custWidth)); | 81 | + var td = d3.select(this), |
93 | - } else if (thElement.classed(tableIcon)) { | 82 | + index = i.toString(); |
94 | - setColWidth(thElement, tdElement, tableIconTdSize); | 83 | + if (cstmWidths.hasOwnProperty(index)) { |
95 | - } else { | 84 | + _width(td, cstmWidths[index]); |
96 | - setColWidth(thElement, tdElement, defaultColWidth); | ||
97 | } | 85 | } |
98 | }); | 86 | }); |
99 | } | 87 | } |
100 | 88 | ||
101 | - // get the size of the window and then subtract the extra space at the top | 89 | + function setHeight(body) { |
102 | - // to get the height of the table | 90 | + var h = height - (mast.mastHeight() + |
103 | - function setTableHeight(thead, tbody) { | 91 | + fs.noPxStyle(d3.select('.tabular-header'), 'height') + |
104 | - var ttlHgt = fs.noPxStyle(d3.select('.tabular-header'), 'height'), | 92 | + fs.noPxStyle(thead, 'height') + pdg); |
105 | - thHgt = fs.noPxStyle(thead, 'height'), | 93 | + body.style('height', h + 'px'); |
106 | - totalHgt = ttlHgt + thHgt + pdg, | ||
107 | - tbleHgt = fs.windowSize(mast.mastHeight() + totalHgt).height; | ||
108 | - | ||
109 | - thead.style('display', 'block'); | ||
110 | - tbody.style({ | ||
111 | - display: 'block', | ||
112 | - height: tbleHgt + 'px', | ||
113 | - overflow: 'auto' | ||
114 | - }); | ||
115 | } | 94 | } |
116 | 95 | ||
117 | - function fixTable(t, th, tb) { | 96 | + findCstmWidths(); |
118 | - setTableWidth(t); | 97 | + setTdWidths(thead); |
119 | - setTableHeight(th, tb); | 98 | + setTdWidths(tbody); |
99 | + setHeight(tbodyDiv); | ||
100 | + | ||
101 | + cstmWidths = {}; | ||
120 | } | 102 | } |
121 | 103 | ||
122 | // Functions for sorting table rows by header | 104 | // Functions for sorting table rows by header |
... | @@ -163,40 +145,42 @@ | ... | @@ -163,40 +145,42 @@ |
163 | } | 145 | } |
164 | 146 | ||
165 | angular.module('onosWidget') | 147 | angular.module('onosWidget') |
166 | - .directive('onosFixedHeader', ['$window', 'FnService', 'MastService', | 148 | + .directive('onosFixedHeader', ['$log','$window', |
167 | - function (_$window_, _fs_, _mast_) { | 149 | + 'FnService', 'MastService', |
150 | + | ||
151 | + function (_$log_, _$window_, _fs_, _mast_) { | ||
168 | return function (scope, element) { | 152 | return function (scope, element) { |
153 | + $log = _$log_; | ||
169 | $window = _$window_; | 154 | $window = _$window_; |
170 | fs = _fs_; | 155 | fs = _fs_; |
171 | mast = _mast_; | 156 | mast = _mast_; |
172 | 157 | ||
173 | var w = angular.element($window), | 158 | var w = angular.element($window), |
174 | table = d3.select(element[0]), | 159 | table = d3.select(element[0]), |
175 | - thead = table.select('thead'), | ||
176 | - tbody = table.select('tbody'), | ||
177 | canAdjust = false; | 160 | canAdjust = false; |
178 | 161 | ||
179 | scope.$watch(function () { | 162 | scope.$watch(function () { |
180 | return { | 163 | return { |
181 | - h: window.innerHeight, | 164 | + h: $window.innerHeight, |
182 | - w: window.innerWidth | 165 | + w: $window.innerWidth |
183 | }; | 166 | }; |
184 | - }, function (newVal) { | 167 | + }, function () { |
185 | - var wsz = fs.windowSize(0, 30); | 168 | + var wsz = fs.windowSize(0, 30), |
186 | - scope.windowHeight = newVal.h; | 169 | + wWidth = wsz.width, |
187 | - scope.windowWidth = newVal.w; | 170 | + wHeight = wsz.height; |
188 | 171 | ||
189 | - // default table size in case no data elements | 172 | + if (!scope.tableData.length) { |
190 | - table.style('width', wsz.width + 'px'); | 173 | + defaultSize(table, wWidth); |
174 | + } | ||
191 | 175 | ||
192 | scope.$on('LastElement', function () { | 176 | scope.$on('LastElement', function () { |
193 | // only adjust the table once it's completely loaded | 177 | // only adjust the table once it's completely loaded |
194 | - fixTable(table, thead, tbody); | 178 | + adjustTable(table, wWidth, wHeight); |
195 | canAdjust = true; | 179 | canAdjust = true; |
196 | }); | 180 | }); |
197 | 181 | ||
198 | if (canAdjust) { | 182 | if (canAdjust) { |
199 | - fixTable(table, thead, tbody); | 183 | + adjustTable(table, wWidth, wHeight); |
200 | } | 184 | } |
201 | }, true); | 185 | }, true); |
202 | 186 | ||
... | @@ -215,16 +199,16 @@ | ... | @@ -215,16 +199,16 @@ |
215 | link: function (scope, element) { | 199 | link: function (scope, element) { |
216 | $log = _$log_; | 200 | $log = _$log_; |
217 | is = _is_; | 201 | is = _is_; |
218 | - var table = d3.select(element[0]); | 202 | + var header = d3.select(element[0]); |
219 | sortIconAPI = is.sortIcons(); | 203 | sortIconAPI = is.sortIcons(); |
220 | 204 | ||
221 | - // when a header is clicked, change its icon tag | 205 | + // when a header is clicked, change its sort direction |
222 | // and get sorting order to send to the server. | 206 | // and get sorting order to send to the server. |
223 | - table.selectAll('th').on('click', function () { | 207 | + header.selectAll('td').on('click', function () { |
224 | - var thElem = d3.select(this); | 208 | + var col = d3.select(this); |
225 | 209 | ||
226 | - if (thElem.attr('sortable') === '') { | 210 | + if (col.attr('sortable') === '') { |
227 | - updateSortDirection(thElem); | 211 | + updateSortDirection(col); |
228 | scope.ctrlCallback({ | 212 | scope.ctrlCallback({ |
229 | requestParams: sortRequestParams() | 213 | requestParams: sortRequestParams() |
230 | }); | 214 | }); |
... | @@ -234,9 +218,9 @@ | ... | @@ -234,9 +218,9 @@ |
234 | }; | 218 | }; |
235 | }]) | 219 | }]) |
236 | 220 | ||
237 | - .factory('TableService', ['$log', 'IconService', | 221 | + .factory('TableService', ['IconService', |
238 | 222 | ||
239 | - function ($log, is) { | 223 | + function (is) { |
240 | sortIconAPI = is.sortIcons(); | 224 | sortIconAPI = is.sortIcons(); |
241 | 225 | ||
242 | return { | 226 | return { | ... | ... |
... | @@ -25,7 +25,6 @@ | ... | @@ -25,7 +25,6 @@ |
25 | 25 | ||
26 | // example params to buildTable: | 26 | // example params to buildTable: |
27 | // { | 27 | // { |
28 | - // self: this, <- controller object | ||
29 | // scope: $scope, <- controller scope | 28 | // scope: $scope, <- controller scope |
30 | // tag: 'device', <- table identifier | 29 | // tag: 'device', <- table identifier |
31 | // selCb: selCb <- row selection callback (optional) | 30 | // selCb: selCb <- row selection callback (optional) |
... | @@ -43,10 +42,10 @@ | ... | @@ -43,10 +42,10 @@ |
43 | resp = o.tag + 'DataResponse', | 42 | resp = o.tag + 'DataResponse', |
44 | onSel = fs.isF(o.selCb); | 43 | onSel = fs.isF(o.selCb); |
45 | 44 | ||
46 | - o.self.tableData = []; | 45 | + o.scope.tableData = []; |
47 | 46 | ||
48 | function respCb(data) { | 47 | function respCb(data) { |
49 | - o.self.tableData = data[root]; | 48 | + o.scope.tableData = data[root]; |
50 | o.scope.$apply(); | 49 | o.scope.$apply(); |
51 | } | 50 | } |
52 | 51 | ... | ... |
1 | <!-- app partial HTML --> | 1 | <!-- app partial HTML --> |
2 | <div id="ov-app"> | 2 | <div id="ov-app"> |
3 | <div class="tabular-header"> | 3 | <div class="tabular-header"> |
4 | - <h2>Applications ({{ctrl.tableData.length}} total)</h2> | 4 | + <h2>Applications ({{tableData.length}} total)</h2> |
5 | <div class="ctrl-btns"> | 5 | <div class="ctrl-btns"> |
6 | <div class="refresh active" | 6 | <div class="refresh active" |
7 | icon icon-size="36" icon-id="refresh" | 7 | icon icon-size="36" icon-id="refresh" |
... | @@ -19,28 +19,30 @@ | ... | @@ -19,28 +19,30 @@ |
19 | </form> | 19 | </form> |
20 | </div> | 20 | </div> |
21 | 21 | ||
22 | - <table class="summary-list" | 22 | + <div class="summary-list" onos-fixed-header> |
23 | - onos-fixed-header | 23 | + |
24 | - onos-sortable-header | 24 | + <div class="table-header" |
25 | - sort-callback="sortCallback(requestParams)"> | 25 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
26 | - <thead> | 26 | + <table> |
27 | <tr> | 27 | <tr> |
28 | - <th colId="state" class="table-icon" sortable></th> | 28 | + <td colId="state" class="table-icon" sortable></td> |
29 | - <th colId="id" sortable>App ID </th> | 29 | + <td colId="id" sortable>App ID </td> |
30 | - <th colId="version" sortable>Version </th> | 30 | + <td colId="version" sortable>Version </td> |
31 | - <th colId="origin" sortable>Origin </th> | 31 | + <td colId="origin" sortable>Origin </td> |
32 | - <th colId="desc" col-width="640px">Description </th> | 32 | + <td colId="desc" col-width="475px">Description </td> |
33 | </tr> | 33 | </tr> |
34 | - </thead> | 34 | + </table> |
35 | + </div> | ||
35 | 36 | ||
36 | - <tbody> | 37 | + <div class="table-body"> |
37 | - <tr ng-hide="ctrl.tableData.length"> | 38 | + <table> |
38 | - <td class="nodata" colspan="5"> | 39 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
40 | + <td colspan="5"> | ||
39 | No Applications found | 41 | No Applications found |
40 | </td> | 42 | </td> |
41 | </tr> | 43 | </tr> |
42 | 44 | ||
43 | - <tr ng-repeat="app in ctrl.tableData" | 45 | + <tr ng-repeat="app in tableData" |
44 | ng-click="selectCallback($event, app)" | 46 | ng-click="selectCallback($event, app)" |
45 | ng-class="{selected: app === sel}" | 47 | ng-class="{selected: app === sel}" |
46 | ng-repeat-done> | 48 | ng-repeat-done> |
... | @@ -52,6 +54,9 @@ | ... | @@ -52,6 +54,9 @@ |
52 | <td>{{app.origin}}</td> | 54 | <td>{{app.origin}}</td> |
53 | <td>{{app.desc}}</td> | 55 | <td>{{app.desc}}</td> |
54 | </tr> | 56 | </tr> |
55 | - </tbody> | ||
56 | </table> | 57 | </table> |
58 | + </div> | ||
59 | + | ||
60 | + </div> | ||
61 | + | ||
57 | </div> | 62 | </div> | ... | ... |
... | @@ -71,7 +71,6 @@ | ... | @@ -71,7 +71,6 @@ |
71 | d3.select('#app-deactivate').on('click', function () { appAction('deactivate'); }); | 71 | d3.select('#app-deactivate').on('click', function () { appAction('deactivate'); }); |
72 | 72 | ||
73 | tbs.buildTable({ | 73 | tbs.buildTable({ |
74 | - self: this, | ||
75 | scope: $scope, | 74 | scope: $scope, |
76 | tag: 'app', | 75 | tag: 'app', |
77 | selCb: selCb | 76 | selCb: selCb | ... | ... |
... | @@ -17,7 +17,7 @@ | ... | @@ -17,7 +17,7 @@ |
17 | <!-- Cluster partial HTML --> | 17 | <!-- Cluster partial HTML --> |
18 | <div id="ov-cluster"> | 18 | <div id="ov-cluster"> |
19 | <div class="tabular-header"> | 19 | <div class="tabular-header"> |
20 | - <h2>Cluster Nodes ({{ctrl.tableData.length}} total)</h2> | 20 | + <h2>Cluster Nodes ({{tableData.length}} total)</h2> |
21 | <div class="ctrl-btns"> | 21 | <div class="ctrl-btns"> |
22 | <div class="refresh active" | 22 | <div class="refresh active" |
23 | icon icon-size="36" icon-id="refresh" | 23 | icon icon-size="36" icon-id="refresh" |
... | @@ -25,28 +25,30 @@ | ... | @@ -25,28 +25,30 @@ |
25 | </div> | 25 | </div> |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | - <table class="summary-list" | 28 | + <div class="summary-list" onos-fixed-header> |
29 | - onos-fixed-header | 29 | + |
30 | - onos-sortable-header | 30 | + <div class="table-header" |
31 | - sort-callback="sortCallback(requestParams)"> | 31 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
32 | - <thead> | 32 | + <table> |
33 | <tr> | 33 | <tr> |
34 | - <th colId="_iconid_state" class="table-icon" sortable></th> | 34 | + <td colId="_iconid_state" class="table-icon" sortable></td> |
35 | - <th colId="id" sortable>ID </th> | 35 | + <td colId="id" sortable>ID </td> |
36 | - <th colId="ip" sortable>IP Address </th> | 36 | + <td colId="ip" sortable>IP Address </td> |
37 | - <th colId="tcp" sortable>TCP Port </th> | 37 | + <td colId="tcp" sortable>TCP Port </td> |
38 | - <th colId="updated" sortable>Last Updated </th> | 38 | + <td colId="updated" sortable>Last Updated </td> |
39 | </tr> | 39 | </tr> |
40 | - </thead> | 40 | + </table> |
41 | + </div> | ||
41 | 42 | ||
42 | - <tbody> | 43 | + <div class="table-body"> |
43 | - <tr ng-hide="ctrl.tableData.length"> | 44 | + <table> |
44 | - <td class="nodata" colspan="5"> | 45 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
46 | + <td colspan="5"> | ||
45 | No Cluster Nodes found | 47 | No Cluster Nodes found |
46 | </td> | 48 | </td> |
47 | </tr> | 49 | </tr> |
48 | 50 | ||
49 | - <tr ng-repeat="node in ctrl.tableData" | 51 | + <tr ng-repeat="node in tableData" |
50 | ng-repeat-done> | 52 | ng-repeat-done> |
51 | <td class="table-icon"> | 53 | <td class="table-icon"> |
52 | <div icon icon-id="{{node._iconid_state}}"></div> | 54 | <div icon icon-id="{{node._iconid_state}}"></div> |
... | @@ -56,6 +58,9 @@ | ... | @@ -56,6 +58,9 @@ |
56 | <td>{{node.tcp}}</td> | 58 | <td>{{node.tcp}}</td> |
57 | <td>{{node.updated}}</td> | 59 | <td>{{node.updated}}</td> |
58 | </tr> | 60 | </tr> |
59 | - </tbody> | ||
60 | </table> | 61 | </table> |
62 | + </div> | ||
63 | + | ||
64 | + </div> | ||
65 | + | ||
61 | </div> | 66 | </div> | ... | ... |
1 | <!-- Device partial HTML --> | 1 | <!-- Device partial HTML --> |
2 | <div id="ov-device"> | 2 | <div id="ov-device"> |
3 | <div class="tabular-header"> | 3 | <div class="tabular-header"> |
4 | - <h2>Devices ({{ctrl.tableData.length}} total)</h2> | 4 | + <h2>Devices ({{tableData.length}} total)</h2> |
5 | <div class="ctrl-btns"> | 5 | <div class="ctrl-btns"> |
6 | <div class="refresh active" | 6 | <div class="refresh active" |
7 | icon icon-size="36" icon-id="refresh" | 7 | icon icon-size="36" icon-id="refresh" |
... | @@ -9,32 +9,34 @@ | ... | @@ -9,32 +9,34 @@ |
9 | </div> | 9 | </div> |
10 | </div> | 10 | </div> |
11 | 11 | ||
12 | - <table class="summary-list" | 12 | + <div class="summary-list" onos-fixed-header> |
13 | - onos-fixed-header | 13 | + |
14 | - onos-sortable-header | 14 | + <div class="table-header" |
15 | - sort-callback="sortCallback(requestParams)"> | 15 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
16 | - <thead> | 16 | + <table> |
17 | <tr> | 17 | <tr> |
18 | - <th colId="available" class="table-icon" sortable></th> | 18 | + <td colId="available" class="table-icon" sortable></td> |
19 | - <th colId="type" class="table-icon" sortable></th> | 19 | + <td colId="type" class="table-icon" sortable></td> |
20 | - <th colId="id" sortable>Device ID </th> | 20 | + <td colId="id" sortable>Device ID </td> |
21 | - <th colId="masterid" sortable>Master Instance </th> | 21 | + <td colId="masterid" sortable>Master Instance </td> |
22 | - <th colId="num_ports" sortable>Ports </th> | 22 | + <td colId="num_ports" sortable>Ports </td> |
23 | - <th colId="mfr" sortable>Vendor </th> | 23 | + <td colId="mfr" sortable>Vendor </td> |
24 | - <th colId="hw" sortable>H/W Version </th> | 24 | + <td colId="hw" sortable>H/W Version </td> |
25 | - <th colId="sw" sortable>S/W Version </th> | 25 | + <td colId="sw" sortable>S/W Version </td> |
26 | - <th colId="protocol" sortable>Protocol </th> | 26 | + <td colId="protocol" sortable>Protocol </td> |
27 | </tr> | 27 | </tr> |
28 | - </thead> | 28 | + </table> |
29 | + </div> | ||
29 | 30 | ||
30 | - <tbody> | 31 | + <div class="table-body"> |
31 | - <tr ng-hide="ctrl.tableData.length"> | 32 | + <table> |
32 | - <td class="nodata" colspan="9"> | 33 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
34 | + <td colspan="9"> | ||
33 | No Devices found | 35 | No Devices found |
34 | </td> | 36 | </td> |
35 | </tr> | 37 | </tr> |
36 | 38 | ||
37 | - <tr ng-repeat="dev in ctrl.tableData" | 39 | + <tr ng-repeat="dev in tableData" |
38 | ng-click="selectCallback($event, dev)" | 40 | ng-click="selectCallback($event, dev)" |
39 | ng-class="{selected: dev === sel}" | 41 | ng-class="{selected: dev === sel}" |
40 | ng-repeat-done> | 42 | ng-repeat-done> |
... | @@ -52,6 +54,9 @@ | ... | @@ -52,6 +54,9 @@ |
52 | <td>{{dev.sw}}</td> | 54 | <td>{{dev.sw}}</td> |
53 | <td>{{dev.protocol}}</td> | 55 | <td>{{dev.protocol}}</td> |
54 | </tr> | 56 | </tr> |
55 | - </tbody> | ||
56 | </table> | 57 | </table> |
58 | + </div> | ||
59 | + | ||
60 | + </div> | ||
61 | + | ||
57 | </div> | 62 | </div> | ... | ... |
... | @@ -25,8 +25,7 @@ | ... | @@ -25,8 +25,7 @@ |
25 | var $log, $scope, fs, mast, ps, wss, is, bns, ns, ttip; | 25 | var $log, $scope, fs, mast, ps, wss, is, bns, ns, ttip; |
26 | 26 | ||
27 | // internal state | 27 | // internal state |
28 | - var self, | 28 | + var detailsPanel, |
29 | - detailsPanel, | ||
30 | pStartY, pHeight, | 29 | pStartY, pHeight, |
31 | top, bottom, iconDiv, | 30 | top, bottom, iconDiv, |
32 | wSize, selRow; | 31 | wSize, selRow; |
... | @@ -183,8 +182,8 @@ | ... | @@ -183,8 +182,8 @@ |
183 | } | 182 | } |
184 | 183 | ||
185 | function respDetailsCb(data) { | 184 | function respDetailsCb(data) { |
186 | - self.panelData = data.details; | 185 | + $scope.panelData = data.details; |
187 | - populateDetails(self.panelData); | 186 | + populateDetails($scope.panelData); |
188 | detailsPanel.show(); | 187 | detailsPanel.show(); |
189 | } | 188 | } |
190 | 189 | ||
... | @@ -219,9 +218,8 @@ | ... | @@ -219,9 +218,8 @@ |
219 | bns = _bns_; | 218 | bns = _bns_; |
220 | ns = _ns_; | 219 | ns = _ns_; |
221 | ttip = _ttip_; | 220 | ttip = _ttip_; |
222 | - self = this; | ||
223 | var handlers = {}; | 221 | var handlers = {}; |
224 | - self.panelData = []; | 222 | + $scope.panelData = []; |
225 | pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height') | 223 | pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height') |
226 | + mast.mastHeight() + topPdg; | 224 | + mast.mastHeight() + topPdg; |
227 | wSize = fs.windowSize(pStartY); | 225 | wSize = fs.windowSize(pStartY); |
... | @@ -238,7 +236,6 @@ | ... | @@ -238,7 +236,6 @@ |
238 | } | 236 | } |
239 | 237 | ||
240 | tbs.buildTable({ | 238 | tbs.buildTable({ |
241 | - self: self, | ||
242 | scope: $scope, | 239 | scope: $scope, |
243 | tag: 'device', | 240 | tag: 'device', |
244 | selCb: selCb | 241 | selCb: selCb | ... | ... |
... | @@ -2,8 +2,8 @@ | ... | @@ -2,8 +2,8 @@ |
2 | <div id="ov-flow"> | 2 | <div id="ov-flow"> |
3 | <div class="tabular-header"> | 3 | <div class="tabular-header"> |
4 | <h2> | 4 | <h2> |
5 | - Flows for Device {{ctrl.devId || "(No device selected)"}} | 5 | + Flows for Device {{devId || "(No device selected)"}} |
6 | - ({{ctrl.tableData.length}} total) | 6 | + ({{tableData.length}} total) |
7 | </h2> | 7 | </h2> |
8 | <div class="ctrl-btns"> | 8 | <div class="ctrl-btns"> |
9 | <div class="refresh active" | 9 | <div class="refresh active" |
... | @@ -12,31 +12,33 @@ | ... | @@ -12,31 +12,33 @@ |
12 | </div> | 12 | </div> |
13 | </div> | 13 | </div> |
14 | 14 | ||
15 | - <table class="summary-list" | 15 | + <div class="summary-list" onos-fixed-header> |
16 | - onos-fixed-header | 16 | + |
17 | - onos-sortable-header | 17 | + <div class="table-header" |
18 | - sort-callback="sortCallback(requestParams)"> | 18 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
19 | - <thead> | 19 | + <table> |
20 | <tr> | 20 | <tr> |
21 | - <th colId="id" sortable>Flow ID </th> | 21 | + <td colId="id" col-width="180px" sortable>Flow ID </td> |
22 | - <th colId="appId" sortable>App ID </th> | 22 | + <td colId="appId" sortable>App ID </td> |
23 | - <th colId="groupId" sortable>Group ID </th> | 23 | + <td colId="groupId" sortable>Group ID </td> |
24 | - <th colId="tableId" sortable>Table ID </th> | 24 | + <td colId="tableId" sortable>Table ID </td> |
25 | - <th colId="priority" sortable>Priority </th> | 25 | + <td colId="priority" sortable>Priority </td> |
26 | - <th colId="timeout" sortable>Timeout </th> | 26 | + <td colId="timeout" sortable>Timeout </td> |
27 | - <th colId="permanent" sortable>Permanent </th> | 27 | + <td colId="permanent" sortable>Permanent </td> |
28 | - <th colId="state" sortable>State </th> | 28 | + <td colId="state" sortable>State </td> |
29 | </tr> | 29 | </tr> |
30 | - </thead> | 30 | + </table> |
31 | + </div> | ||
31 | 32 | ||
32 | - <tbody> | 33 | + <div class="table-body"> |
33 | - <tr ng-hide="ctrl.tableData.length"> | 34 | + <table> |
34 | - <td class="nodata" colspan="8"> | 35 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
36 | + <td colspan="8"> | ||
35 | No Flows found | 37 | No Flows found |
36 | </td> | 38 | </td> |
37 | </tr> | 39 | </tr> |
38 | 40 | ||
39 | - <tr ng-repeat-start="flow in ctrl.tableData"> | 41 | + <tr ng-repeat-start="flow in tableData"> |
40 | <td>{{flow.id}}</td> | 42 | <td>{{flow.id}}</td> |
41 | <td>{{flow.appId}}</td> | 43 | <td>{{flow.appId}}</td> |
42 | <td>{{flow.groupId}}</td> | 44 | <td>{{flow.groupId}}</td> |
... | @@ -46,12 +48,16 @@ | ... | @@ -46,12 +48,16 @@ |
46 | <td>{{flow.permanent}}</td> | 48 | <td>{{flow.permanent}}</td> |
47 | <td>{{flow.state}}</td> | 49 | <td>{{flow.state}}</td> |
48 | </tr> | 50 | </tr> |
49 | - <tr> | 51 | + <tr class="ignore-width"> |
50 | <td class="selector" colspan="8">{{flow.selector}}</td> | 52 | <td class="selector" colspan="8">{{flow.selector}}</td> |
51 | </tr> | 53 | </tr> |
52 | - <tr ng-repeat-end ng-repeat-done> | 54 | + <tr class="ignore-width" |
55 | + ng-repeat-end ng-repeat-done> | ||
53 | <td class="treatment" colspan="8">{{flow.treatment}}</td> | 56 | <td class="treatment" colspan="8">{{flow.treatment}}</td> |
54 | </tr> | 57 | </tr> |
55 | - </tbody> | ||
56 | </table> | 58 | </table> |
59 | + </div> | ||
60 | + | ||
61 | + </div> | ||
62 | + | ||
57 | </div> | 63 | </div> | ... | ... |
... | @@ -30,8 +30,7 @@ | ... | @@ -30,8 +30,7 @@ |
30 | 'FnService', 'TableService', 'TableBuilderService', | 30 | 'FnService', 'TableService', 'TableBuilderService', |
31 | 31 | ||
32 | function (_$log_, _$scope_, _$location_, _fs_, _ts_, _tbs_) { | 32 | function (_$log_, _$scope_, _$location_, _fs_, _ts_, _tbs_) { |
33 | - var self = this, | 33 | + var params; |
34 | - params; | ||
35 | $log = _$log_; | 34 | $log = _$log_; |
36 | $scope = _$scope_; | 35 | $scope = _$scope_; |
37 | $location = _$location_; | 36 | $location = _$location_; |
... | @@ -41,11 +40,10 @@ | ... | @@ -41,11 +40,10 @@ |
41 | 40 | ||
42 | params = $location.search(); | 41 | params = $location.search(); |
43 | if (params.hasOwnProperty('devId')) { | 42 | if (params.hasOwnProperty('devId')) { |
44 | - self.devId = params['devId']; | 43 | + $scope.devId = params['devId']; |
45 | } | 44 | } |
46 | 45 | ||
47 | tbs.buildTable({ | 46 | tbs.buildTable({ |
48 | - self: self, | ||
49 | scope: $scope, | 47 | scope: $scope, |
50 | tag: 'flow', | 48 | tag: 'flow', |
51 | query: params | 49 | query: params | ... | ... |
1 | <!-- Host partial HTML --> | 1 | <!-- Host partial HTML --> |
2 | <div id="ov-host"> | 2 | <div id="ov-host"> |
3 | <div class="tabular-header"> | 3 | <div class="tabular-header"> |
4 | - <h2>Hosts ({{ctrl.tableData.length}} total)</h2> | 4 | + <h2>Hosts ({{tableData.length}} total)</h2> |
5 | <div class="ctrl-btns"> | 5 | <div class="ctrl-btns"> |
6 | <div class="refresh active" | 6 | <div class="refresh active" |
7 | icon icon-size="36" icon-id="refresh" | 7 | icon icon-size="36" icon-id="refresh" |
... | @@ -9,29 +9,31 @@ | ... | @@ -9,29 +9,31 @@ |
9 | </div> | 9 | </div> |
10 | </div> | 10 | </div> |
11 | 11 | ||
12 | - <table class="summary-list" | 12 | + <div class="summary-list" onos-fixed-header> |
13 | - onos-fixed-header | 13 | + |
14 | - onos-sortable-header | 14 | + <div class="table-header" |
15 | - sort-callback="sortCallback(requestParams)"> | 15 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
16 | - <thead> | 16 | + <table> |
17 | <tr> | 17 | <tr> |
18 | - <th colId="type" class="table-icon" sortable></th> | 18 | + <td colId="type" class="table-icon" sortable></td> |
19 | - <th colId="id" sortable>Host ID </th> | 19 | + <td colId="id" sortable>Host ID </td> |
20 | - <th colId="mac" sortable>MAC Address </th> | 20 | + <td colId="mac" sortable>MAC Address </td> |
21 | - <th colId="vlan" sortable>VLAN ID </th> | 21 | + <td colId="vlan" sortable>VLAN ID </td> |
22 | - <th colId="ips" sortable>IP Addresses </th> | 22 | + <td colId="ips" sortable>IP Addresses </td> |
23 | - <th colId="location" sortable>Location </th> | 23 | + <td colId="location" sortable>Location </td> |
24 | </tr> | 24 | </tr> |
25 | - </thead> | 25 | + </table> |
26 | + </div> | ||
26 | 27 | ||
27 | - <tbody> | 28 | + <div class="table-body"> |
28 | - <tr ng-hide="ctrl.tableData.length"> | 29 | + <table> |
29 | - <td class="nodata" colspan="6"> | 30 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
31 | + <td colspan="6"> | ||
30 | No Hosts found | 32 | No Hosts found |
31 | </td> | 33 | </td> |
32 | </tr> | 34 | </tr> |
33 | 35 | ||
34 | - <tr ng-repeat="host in ctrl.tableData" | 36 | + <tr ng-repeat="host in tableData" |
35 | ng-repeat-done> | 37 | ng-repeat-done> |
36 | <td class="table-icon"> | 38 | <td class="table-icon"> |
37 | <div icon icon-id="{{host._iconid_type}}"></div> | 39 | <div icon icon-id="{{host._iconid_type}}"></div> |
... | @@ -42,6 +44,9 @@ | ... | @@ -42,6 +44,9 @@ |
42 | <td>{{host.ips}}</td> | 44 | <td>{{host.ips}}</td> |
43 | <td>{{host.location}}</td> | 45 | <td>{{host.location}}</td> |
44 | </tr> | 46 | </tr> |
45 | - </tbody> | ||
46 | </table> | 47 | </table> |
48 | + </div> | ||
49 | + | ||
50 | + </div> | ||
51 | + | ||
47 | </div> | 52 | </div> | ... | ... |
... | @@ -17,34 +17,37 @@ | ... | @@ -17,34 +17,37 @@ |
17 | <!-- Intent partial HTML --> | 17 | <!-- Intent partial HTML --> |
18 | <div id="ov-intent"> | 18 | <div id="ov-intent"> |
19 | <div class="tabular-header"> | 19 | <div class="tabular-header"> |
20 | - <h2>Intents ({{ctrl.tableData.length}} total)</h2> | 20 | + <h2>Intents ({{tableData.length}} total)</h2> |
21 | <div class="ctrl-btns"> | 21 | <div class="ctrl-btns"> |
22 | <div class="refresh active" | 22 | <div class="refresh active" |
23 | icon icon-size="36" icon-id="refresh" | 23 | icon icon-size="36" icon-id="refresh" |
24 | ng-click="refresh()"></div> | 24 | ng-click="refresh()"></div> |
25 | </div> | 25 | </div> |
26 | </div> | 26 | </div> |
27 | - <table class="summary-list" | 27 | + |
28 | - onos-fixed-header | 28 | + <div class="summary-list" onos-fixed-header> |
29 | - onos-sortable-header | 29 | + |
30 | - sort-callback="sortCallback(requestParams)"> | 30 | + <div class="table-header" |
31 | - <thead> | 31 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
32 | + <table> | ||
32 | <tr> | 33 | <tr> |
33 | - <th colId="appId" sortable>Application ID </th> | 34 | + <td colId="appId" sortable>Application ID </td> |
34 | - <th colId="key" sortable>Key </th> | 35 | + <td colId="key" sortable>Key </td> |
35 | - <th colId="type" sortable>Type </th> | 36 | + <td colId="type" sortable>Type </td> |
36 | - <th colId="priority" sortable>Priority </th> | 37 | + <td colId="priority" sortable>Priority </td> |
37 | </tr> | 38 | </tr> |
38 | - </thead> | 39 | + </table> |
40 | + </div> | ||
39 | 41 | ||
40 | - <tbody> | 42 | + <div class="table-body"> |
41 | - <tr ng-hide="ctrl.tableData.length"> | 43 | + <table> |
42 | - <td class="nodata" colspan="4"> | 44 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
45 | + <td colspan="4"> | ||
43 | No Intents found | 46 | No Intents found |
44 | </td> | 47 | </td> |
45 | </tr> | 48 | </tr> |
46 | 49 | ||
47 | - <tr ng-repeat-start="intent in ctrl.tableData"> | 50 | + <tr ng-repeat-start="intent in tableData"> |
48 | <td>{{intent.appId}}</td> | 51 | <td>{{intent.appId}}</td> |
49 | <td>{{intent.key}}</td> | 52 | <td>{{intent.key}}</td> |
50 | <td>{{intent.type}}</td> | 53 | <td>{{intent.type}}</td> |
... | @@ -56,6 +59,9 @@ | ... | @@ -56,6 +59,9 @@ |
56 | <tr ng-repeat-end ng-repeat-done> | 59 | <tr ng-repeat-end ng-repeat-done> |
57 | <td class="details" colspan="4">{{intent.details}}</td> | 60 | <td class="details" colspan="4">{{intent.details}}</td> |
58 | </tr> | 61 | </tr> |
59 | - </tbody> | ||
60 | </table> | 62 | </table> |
63 | + </div> | ||
64 | + | ||
65 | + </div> | ||
66 | + | ||
61 | </div> | 67 | </div> | ... | ... |
... | @@ -17,7 +17,7 @@ | ... | @@ -17,7 +17,7 @@ |
17 | <!-- Link partial HTML --> | 17 | <!-- Link partial HTML --> |
18 | <div id="ov-link"> | 18 | <div id="ov-link"> |
19 | <div class="tabular-header"> | 19 | <div class="tabular-header"> |
20 | - <h2>Links ({{ctrl.tableData.length}} total)</h2> | 20 | + <h2>Links ({{tableData.length}} total)</h2> |
21 | <div class="ctrl-btns"> | 21 | <div class="ctrl-btns"> |
22 | <div class="refresh active" | 22 | <div class="refresh active" |
23 | icon icon-size="36" icon-id="refresh" | 23 | icon icon-size="36" icon-id="refresh" |
... | @@ -25,29 +25,31 @@ | ... | @@ -25,29 +25,31 @@ |
25 | </div> | 25 | </div> |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | - <table class="summary-list" | 28 | + <div class="summary-list" onos-fixed-header> |
29 | - onos-fixed-header | 29 | + |
30 | - onos-sortable-header | 30 | + <div class="table-header" |
31 | - sort-callback="sortCallback(requestParams)"> | 31 | + onos-sortable-header sort-callback="sortCallback(requestParams)"> |
32 | - <thead> | 32 | + <table> |
33 | <tr> | 33 | <tr> |
34 | - <th colId="_iconid_state" class="table-icon" sortable></th> | 34 | + <td colId="_iconid_state" class="table-icon" sortable></td> |
35 | - <th colId="one" sortable>Port 1 </th> | 35 | + <td colId="one" sortable>Port 1 </td> |
36 | - <th colId="two" sortable>Port 2 </th> | 36 | + <td colId="two" sortable>Port 2 </td> |
37 | - <th colId="type" sortable>Type </th> | 37 | + <td colId="type" sortable>Type </td> |
38 | - <th colId="direction" sortable>Direction </th> | 38 | + <td colId="direction" sortable>Direction </td> |
39 | - <th colId="durable" sortable>Durable </th> | 39 | + <td colId="durable" sortable>Durable </td> |
40 | </tr> | 40 | </tr> |
41 | - </thead> | 41 | + </table> |
42 | + </div> | ||
42 | 43 | ||
43 | - <tbody> | 44 | + <div class="table-body"> |
44 | - <tr ng-hide="ctrl.tableData.length"> | 45 | + <table> |
45 | - <td class="nodata" colspan="6"> | 46 | + <tr ng-hide="tableData.length" class="no-data ignore-width"> |
47 | + <td colspan="6"> | ||
46 | No Links found | 48 | No Links found |
47 | </td> | 49 | </td> |
48 | </tr> | 50 | </tr> |
49 | 51 | ||
50 | - <tr ng-repeat="link in ctrl.tableData" | 52 | + <tr ng-repeat="link in tableData" |
51 | ng-repeat-done> | 53 | ng-repeat-done> |
52 | <td class="table-icon"> | 54 | <td class="table-icon"> |
53 | <div icon icon-id="{{link._iconid_state}}"></div> | 55 | <div icon icon-id="{{link._iconid_state}}"></div> |
... | @@ -58,6 +60,9 @@ | ... | @@ -58,6 +60,9 @@ |
58 | <td>{{link.direction}}</td> | 60 | <td>{{link.direction}}</td> |
59 | <td>{{link.durable}}</td> | 61 | <td>{{link.durable}}</td> |
60 | </tr> | 62 | </tr> |
61 | - </tbody> | ||
62 | </table> | 63 | </table> |
64 | + </div> | ||
65 | + | ||
66 | + </div> | ||
67 | + | ||
63 | </div> | 68 | </div> | ... | ... |
... | @@ -20,71 +20,59 @@ | ... | @@ -20,71 +20,59 @@ |
20 | describe('factory: fw/widget/table.js', function () { | 20 | describe('factory: fw/widget/table.js', function () { |
21 | var $log, $compile, $rootScope, | 21 | var $log, $compile, $rootScope, |
22 | fs, ts, mast, is, | 22 | fs, ts, mast, is, |
23 | - scope, compiled, | 23 | + scope, |
24 | - table, thead, tbody, mockHeader, | 24 | + containerDiv, |
25 | - mockH2Height = 20, | 25 | + headerDiv, bodyDiv, |
26 | - mockMastHeight = 20, | 26 | + header, body, |
27 | - mockHeaderHeight = mockH2Height + mockMastHeight, | 27 | + mockHeader, |
28 | - tableIconTdSize = 100, | 28 | + mockHeaderHeight = 40; |
29 | - numTestElems = 4; | ||
30 | 29 | ||
31 | var onosFixedHeaderTags = | 30 | var onosFixedHeaderTags = |
32 | - '<table onos-fixed-header>' + | 31 | + '<div class="summary-list" onos-fixed-header>' + |
33 | - '<thead style="height:27px;">' + | 32 | + '<div class="table-header">' + |
33 | + '<table>' + | ||
34 | '<tr>' + | 34 | '<tr>' + |
35 | - '<th></th>' + | 35 | + '<td colId="type" class="table-icon"></td>' + |
36 | - '<th>Device ID </th>' + | 36 | + '<td colId="id">Host ID </td>' + |
37 | - '<th col-width="100px">H/W Version </th>' + | 37 | + '<td colId="mac" sortable>MAC Address </td>' + |
38 | - '<th>S/W Version </th>' + | 38 | + '<td colId="location" col-width="110px">Location </td>' + |
39 | '</tr>' + | 39 | '</tr>' + |
40 | - '</thead>' + | 40 | + '</table>' + |
41 | - '<tbody>' + | 41 | + '</div>' + |
42 | - '<tr>' + | 42 | + |
43 | - '<td colspan="4">' + | 43 | + '<div class="table-body">' + |
44 | - 'No Devices found' + | 44 | + '<table>' + |
45 | - '</td>' + | 45 | + '<tr class="ignore-width">' + |
46 | + '<td class="not-picked"></td>' + | ||
46 | '</tr>' + | 47 | '</tr>' + |
47 | '<tr>' + | 48 | '<tr>' + |
48 | - '<td class="table-icon">' + | 49 | + '<td class="table-icon">Some Icon</td>' + |
49 | - '<div icon icon-id="{{dev._iconid_available}}">' + | ||
50 | - '</div>' + | ||
51 | - '</td>' + | ||
52 | '<td>Some ID</td>' + | 50 | '<td>Some ID</td>' + |
53 | - '<td>Some HW</td>' + | 51 | + '<td>Some MAC Address</td>' + |
54 | - '<td>Some Software</td>' + | 52 | + '<td>Some Location</td>' + |
55 | '</tr>' + | 53 | '</tr>' + |
56 | - '</tbody>' + | 54 | + '</table>' + |
57 | - '</table>', | 55 | + '</div>' + |
56 | + '</div>', | ||
58 | 57 | ||
59 | onosSortableHeaderTags = | 58 | onosSortableHeaderTags = |
60 | - '<table onos-sortable-header ' + | 59 | + '<div onos-sortable-header ' + |
61 | 'sort-callback="sortCallback(requestParams)">' + | 60 | 'sort-callback="sortCallback(requestParams)">' + |
62 | - '<thead>' + | 61 | + '<table>' + |
63 | '<tr>' + | 62 | '<tr>' + |
64 | - '<th colId="available"></th>' + | 63 | + '<td colId="type"></td>' + |
65 | - '<th colId="id" sortable>Device ID </th>' + | 64 | + '<td colId="id" sortable>Host ID </td>' + |
66 | - '<th colId="hw" sortable>H/W Version </th>' + | 65 | + '<td colId="mac" sortable>MAC Address </td>' + |
67 | - '<th colId="sw" sortable>S/W Version </th>' + | 66 | + '<td colId="location" sortable>Location </td>' + |
68 | '</tr>' + | 67 | '</tr>' + |
69 | - '</thead>' + | 68 | + '</table>' + |
70 | - '<tbody>' + | 69 | + '</div>'; |
71 | - '<tr>' + | ||
72 | - '<td>' + | ||
73 | - '<div icon icon-id="{{dev._iconid_available}}">' + | ||
74 | - '</div>' + | ||
75 | - '</td>' + | ||
76 | - '<td>Some ID</td>' + | ||
77 | - '<td>Some HW</td>' + | ||
78 | - '<td>Some Software</td>' + | ||
79 | - '</tr>' + | ||
80 | - '</tbody>' + | ||
81 | - '</table>'; | ||
82 | 70 | ||
83 | beforeEach(module('onosWidget', 'onosUtil', 'onosMast', 'onosSvg')); | 71 | beforeEach(module('onosWidget', 'onosUtil', 'onosMast', 'onosSvg')); |
84 | 72 | ||
85 | var mockWindow = { | 73 | var mockWindow = { |
86 | - innerWidth: 400, | 74 | + innerWidth: 600, |
87 | - innerHeight: 200, | 75 | + innerHeight: 400, |
88 | navigator: { | 76 | navigator: { |
89 | userAgent: 'defaultUA' | 77 | userAgent: 'defaultUA' |
90 | }, | 78 | }, |
... | @@ -111,23 +99,44 @@ describe('factory: fw/widget/table.js', function () { | ... | @@ -111,23 +99,44 @@ describe('factory: fw/widget/table.js', function () { |
111 | 99 | ||
112 | beforeEach(function () { | 100 | beforeEach(function () { |
113 | scope = $rootScope.$new(); | 101 | scope = $rootScope.$new(); |
102 | + scope.tableData = []; | ||
114 | }); | 103 | }); |
115 | 104 | ||
105 | + // Note: dummy header so that d3 doesn't trip up. | ||
106 | + // $compile has to be used on the directive tag element, so it can't | ||
107 | + // be included in the tag strings declared above. | ||
116 | beforeEach(function () { | 108 | beforeEach(function () { |
117 | mockHeader = d3.select('body') | 109 | mockHeader = d3.select('body') |
118 | .append('h2') | 110 | .append('h2') |
119 | .classed('tabular-header', true) | 111 | .classed('tabular-header', true) |
120 | - .style('height', mockHeaderHeight + 'px') | 112 | + .style({ |
121 | - .html('Some Header'); | 113 | + height: mockHeaderHeight + 'px', |
114 | + margin: 0, | ||
115 | + padding: 0 | ||
116 | + }) | ||
117 | + .text('Some Header'); | ||
122 | }); | 118 | }); |
123 | 119 | ||
124 | afterEach(function () { | 120 | afterEach(function () { |
125 | - table = null; | 121 | + containerDiv = undefined; |
126 | - thead = null; | 122 | + headerDiv = undefined; |
127 | - tbody = null; | 123 | + bodyDiv = undefined; |
124 | + header = undefined; | ||
125 | + body = undefined; | ||
128 | mockHeader.remove(); | 126 | mockHeader.remove(); |
129 | }); | 127 | }); |
130 | 128 | ||
129 | + function populateTableData() { | ||
130 | + scope.tableData = [ | ||
131 | + { | ||
132 | + type: 'endstation', | ||
133 | + id: '1234', | ||
134 | + mac: '00:00:03', | ||
135 | + location: 'USA' | ||
136 | + } | ||
137 | + ]; | ||
138 | + } | ||
139 | + | ||
131 | it('should define TableBuilderService', function () { | 140 | it('should define TableBuilderService', function () { |
132 | expect(ts).toBeDefined(); | 141 | expect(ts).toBeDefined(); |
133 | }); | 142 | }); |
... | @@ -138,83 +147,119 @@ describe('factory: fw/widget/table.js', function () { | ... | @@ -138,83 +147,119 @@ describe('factory: fw/widget/table.js', function () { |
138 | ])).toBeTruthy(); | 147 | ])).toBeTruthy(); |
139 | }); | 148 | }); |
140 | 149 | ||
141 | - function compileTable() { | 150 | + function compile(elem) { |
142 | - compiled = $compile(table); | 151 | + var compiled = $compile(elem); |
143 | compiled(scope); | 152 | compiled(scope); |
144 | scope.$digest(); | 153 | scope.$digest(); |
145 | } | 154 | } |
146 | 155 | ||
147 | - function verifyGivenTags(dirName) { | 156 | + function selectTables() { |
148 | - expect(table).toBeDefined(); | 157 | + expect(containerDiv.find('div').length).toBe(2); |
149 | - expect(table.attr(dirName)).toBe(''); | 158 | + |
159 | + headerDiv = angular.element(containerDiv[0].querySelector('.table-header')); | ||
160 | + expect(headerDiv.length).toBe(1); | ||
161 | + | ||
162 | + bodyDiv = angular.element(containerDiv[0].querySelector('.table-body')); | ||
163 | + expect(bodyDiv.length).toBe(1); | ||
164 | + | ||
165 | + header = headerDiv.find('table'); | ||
166 | + expect(header.length).toBe(1); | ||
167 | + | ||
168 | + body = bodyDiv.find('table'); | ||
169 | + expect(body.length).toBe(1); | ||
170 | + } | ||
150 | 171 | ||
151 | - thead = table.find('thead'); | 172 | + function verifyGivenTags(dirName, div) { |
152 | - expect(thead).toBeDefined(); | 173 | + expect(div).toBeDefined(); |
153 | - tbody = table.find('tbody'); | 174 | + expect(div.attr(dirName)).toBe(''); |
154 | - expect(tbody).toBeDefined(); | ||
155 | } | 175 | } |
156 | 176 | ||
157 | - function verifyCssDisplay() { | 177 | + function verifyDefaultSize() { |
158 | - var padding = 21, // bottom table constant 12 + mastPadding(?) 9 | 178 | + expect(header.css('width')).toBe('570px'); |
159 | - tableHeight = fs.windowSize(mockHeaderHeight).height - | 179 | + expect(body.css('width')).toBe('570px'); |
160 | - (fs.noPx(table.find('thead').css('height')) + padding); | 180 | + } |
161 | 181 | ||
162 | - expect(thead.css('display')).toBe('block'); | 182 | + function verifyHeight() { |
163 | - expect(tbody.css('display')).toBe('block'); | 183 | + var padding = 12, |
164 | - expect(tbody.css('height')).toBe(tableHeight + 'px'); | 184 | + mastHeight = 36, |
165 | - expect(tbody.css('overflow')).toBe('auto'); | 185 | + tableHeight = (mockWindow.innerHeight - mockHeaderHeight) - |
186 | + (fs.noPx(headerDiv.css('height')) + mastHeight + padding); | ||
166 | 187 | ||
167 | - // TODO: investigate why math for calculating the height works better | 188 | + expect(bodyDiv.css('height')).toBe(tableHeight + 'px'); |
168 | - // in the browser window (thead height is 0 in this test?) | ||
169 | } | 189 | } |
170 | 190 | ||
171 | function verifyColWidth() { | 191 | function verifyColWidth() { |
172 | - var winWidth = fs.windowSize().width, | 192 | + var hdrs = header.find('td'), |
173 | - colWidth, thElems, tr, tdElem; | 193 | + cols = body.find('td'); |
174 | - | 194 | + |
175 | - colWidth = Math.floor(winWidth / numTestElems); | 195 | + expect(angular.element(hdrs[0]).css('width')).toBe('33px'); |
176 | - | 196 | + expect(angular.element(hdrs[3]).css('width')).toBe('110px'); |
177 | - thElems = thead.find('th'); | 197 | + |
178 | - | 198 | + expect(angular.element(cols[1]).css('width')).toBe('33px'); |
179 | - angular.forEach(thElems, function (thElem, i) { | 199 | + expect(angular.element(cols[4]).css('width')).toBe('110px'); |
180 | - thElem = angular.element(thElems[i]); | ||
181 | - tr = angular.element(tbody.find('tr').eq(1)); | ||
182 | - tdElem = angular.element(tr.find('td').eq(i)); | ||
183 | - var custWidth = thElem.attr('col-width'); | ||
184 | - | ||
185 | - if (custWidth) { | ||
186 | - expect(thElem.css('width')).toBe(custWidth); | ||
187 | - expect(tdElem.css('width')).toBe(custWidth); | ||
188 | - } else if (tdElem.attr('class') === 'table-icon') { | ||
189 | - expect(thElem.css('width')).toBe(tableIconTdSize + 'px'); | ||
190 | - expect(tdElem.css('width')).toBe(tableIconTdSize + 'px'); | ||
191 | - } else { | ||
192 | - expect(thElem.css('width')).toBe(colWidth + 'px'); | ||
193 | - expect(tdElem.css('width')).toBe(colWidth + 'px'); | ||
194 | - } | ||
195 | - }); | ||
196 | } | 200 | } |
197 | 201 | ||
198 | - function verifyCallbacks(thElems) { | 202 | + function verifyCallbacks(h) { |
199 | expect(scope.sortCallback).not.toHaveBeenCalled(); | 203 | expect(scope.sortCallback).not.toHaveBeenCalled(); |
200 | 204 | ||
201 | - // first test header has no 'sortable' attr | 205 | + h[0].click(); |
202 | - thElems[0].click(); | ||
203 | expect(scope.sortCallback).not.toHaveBeenCalled(); | 206 | expect(scope.sortCallback).not.toHaveBeenCalled(); |
204 | 207 | ||
205 | - // the other headers have 'sortable' | 208 | + h[1].click(); |
206 | - for(var i = 1; i < numTestElems; i += 1) { | 209 | + expect(scope.sortCallback).toHaveBeenCalledWith({ |
207 | - thElems[i].click(); | 210 | + sortCol: 'id', |
208 | - expect(scope.sortCallback).toHaveBeenCalled(); | 211 | + sortDir: 'asc' |
209 | - } | 212 | + }); |
213 | + h[1].click(); | ||
214 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
215 | + sortCol: 'id', | ||
216 | + sortDir: 'desc' | ||
217 | + }); | ||
218 | + h[1].click(); | ||
219 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
220 | + sortCol: 'id', | ||
221 | + sortDir: 'asc' | ||
222 | + }); | ||
223 | + | ||
224 | + h[2].click(); | ||
225 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
226 | + sortCol: 'mac', | ||
227 | + sortDir: 'asc' | ||
228 | + }); | ||
229 | + h[2].click(); | ||
230 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
231 | + sortCol: 'mac', | ||
232 | + sortDir: 'desc' | ||
233 | + }); | ||
234 | + h[2].click(); | ||
235 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
236 | + sortCol: 'mac', | ||
237 | + sortDir: 'asc' | ||
238 | + }); | ||
239 | + | ||
240 | + h[3].click(); | ||
241 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
242 | + sortCol: 'location', | ||
243 | + sortDir: 'asc' | ||
244 | + }); | ||
245 | + h[3].click(); | ||
246 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
247 | + sortCol: 'location', | ||
248 | + sortDir: 'desc' | ||
249 | + }); | ||
250 | + h[3].click(); | ||
251 | + expect(scope.sortCallback).toHaveBeenCalledWith({ | ||
252 | + sortCol: 'location', | ||
253 | + sortDir: 'asc' | ||
254 | + }); | ||
210 | } | 255 | } |
211 | 256 | ||
212 | - function verifyIcons(thElems) { | 257 | + function verifyIcons(h) { |
213 | - var currentTh, div; | 258 | + var currH, div; |
214 | - // make sure it has the correct icon after clicking | 259 | + |
215 | - thElems[1].click(); | 260 | + h[1].click(); |
216 | - currentTh = angular.element(thElems[1]); | 261 | + currH = angular.element(h[1]); |
217 | - div = currentTh.find('div'); | 262 | + div = currH.find('div'); |
218 | expect(div.html()).toBe( | 263 | expect(div.html()).toBe( |
219 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + | 264 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + |
220 | '50 50"><g class="icon upArrow"><rect width="50" height="50" ' + | 265 | '50 50"><g class="icon upArrow"><rect width="50" height="50" ' + |
... | @@ -222,8 +267,8 @@ describe('factory: fw/widget/table.js', function () { | ... | @@ -222,8 +267,8 @@ describe('factory: fw/widget/table.js', function () { |
222 | 'xlink="http://www.w3.org/1999/xlink" xlink:href="#triangleUp">' + | 267 | 'xlink="http://www.w3.org/1999/xlink" xlink:href="#triangleUp">' + |
223 | '</use></g></svg>' | 268 | '</use></g></svg>' |
224 | ); | 269 | ); |
225 | - thElems[1].click(); | 270 | + h[1].click(); |
226 | - div = currentTh.find('div'); | 271 | + div = currH.find('div'); |
227 | expect(div.html()).toBe( | 272 | expect(div.html()).toBe( |
228 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + | 273 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + |
229 | '50 50"><g class="icon downArrow"><rect width="50" height="50" ' + | 274 | '50 50"><g class="icon downArrow"><rect width="50" height="50" ' + |
... | @@ -232,14 +277,14 @@ describe('factory: fw/widget/table.js', function () { | ... | @@ -232,14 +277,14 @@ describe('factory: fw/widget/table.js', function () { |
232 | '</use></g></svg>' | 277 | '</use></g></svg>' |
233 | ); | 278 | ); |
234 | 279 | ||
235 | - thElems[2].click(); | 280 | + h[2].click(); |
236 | - div = currentTh.children(); | 281 | + div = currH.children(); |
237 | // clicked on a new element, so the previous icon should have been deleted | 282 | // clicked on a new element, so the previous icon should have been deleted |
238 | expect(div.html()).toBeFalsy(); | 283 | expect(div.html()).toBeFalsy(); |
239 | 284 | ||
240 | // the new element should have the ascending icon | 285 | // the new element should have the ascending icon |
241 | - currentTh = angular.element(thElems[2]); | 286 | + currH = angular.element(h[2]); |
242 | - div = currentTh.children(); | 287 | + div = currH.children(); |
243 | expect(div.html()).toBe( | 288 | expect(div.html()).toBe( |
244 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + | 289 | '<svg class="embeddedIcon" width="10" height="10" viewBox="0 0 ' + |
245 | '50 50"><g class="icon upArrow"><rect width="50" height="50" ' + | 290 | '50 50"><g class="icon upArrow"><rect width="50" height="50" ' + |
... | @@ -250,31 +295,42 @@ describe('factory: fw/widget/table.js', function () { | ... | @@ -250,31 +295,42 @@ describe('factory: fw/widget/table.js', function () { |
250 | } | 295 | } |
251 | 296 | ||
252 | it('should affirm that onos-fixed-header is working', function () { | 297 | it('should affirm that onos-fixed-header is working', function () { |
253 | - table = angular.element(onosFixedHeaderTags); | 298 | + containerDiv = angular.element(onosFixedHeaderTags); |
299 | + | ||
300 | + compile(containerDiv); | ||
301 | + | ||
302 | + verifyGivenTags('onos-fixed-header', containerDiv); | ||
303 | + selectTables(); | ||
304 | + verifyDefaultSize(); | ||
254 | 305 | ||
255 | - compileTable(); | 306 | + populateTableData(); |
256 | - verifyGivenTags('onos-fixed-header'); | ||
257 | 307 | ||
258 | - // table will not be fixed unless it receives the 'LastElement' event | ||
259 | scope.$emit('LastElement'); | 308 | scope.$emit('LastElement'); |
260 | scope.$digest(); | 309 | scope.$digest(); |
261 | 310 | ||
262 | - verifyCssDisplay(); | 311 | + verifyHeight(); |
312 | + verifyColWidth(); | ||
313 | + | ||
314 | + mockWindow.innerHeight = 300; | ||
315 | + scope.$digest(); | ||
316 | + verifyHeight(); | ||
317 | + | ||
318 | + mockWindow.innerWidth = 500; | ||
319 | + scope.$digest(); | ||
263 | verifyColWidth(); | 320 | verifyColWidth(); |
264 | }); | 321 | }); |
265 | 322 | ||
266 | it('should affirm that onos-sortable-header is working', function () { | 323 | it('should affirm that onos-sortable-header is working', function () { |
267 | - var thElems; | 324 | + headerDiv = angular.element(onosSortableHeaderTags); |
268 | - table = angular.element(onosSortableHeaderTags); | ||
269 | 325 | ||
270 | - compileTable(); | 326 | + compile(headerDiv); |
271 | - verifyGivenTags('onos-sortable-header'); | 327 | + verifyGivenTags('onos-sortable-header', headerDiv); |
272 | 328 | ||
273 | scope.sortCallback = jasmine.createSpy('sortCallback'); | 329 | scope.sortCallback = jasmine.createSpy('sortCallback'); |
274 | 330 | ||
275 | - thElems = thead.find('th'); | 331 | + header = headerDiv.find('td'); |
276 | - verifyCallbacks(thElems); | 332 | + verifyCallbacks(header); |
277 | - verifyIcons(thElems); | 333 | + verifyIcons(header); |
278 | }); | 334 | }); |
279 | 335 | ||
280 | // Note: testing resetSortIcons isn't feasible because due to the nature of | 336 | // Note: testing resetSortIcons isn't feasible because due to the nature of | ... | ... |
... | @@ -48,7 +48,6 @@ describe('factory: fw/widget/tableBuilder.js', function () { | ... | @@ -48,7 +48,6 @@ describe('factory: fw/widget/tableBuilder.js', function () { |
48 | 48 | ||
49 | beforeEach(function () { | 49 | beforeEach(function () { |
50 | mockObj = { | 50 | mockObj = { |
51 | - self: {}, | ||
52 | scope: $rootScope.$new(), | 51 | scope: $rootScope.$new(), |
53 | tag: 'foo', | 52 | tag: 'foo', |
54 | selCb: mockSelCb | 53 | selCb: mockSelCb |
... | @@ -78,10 +77,10 @@ describe('factory: fw/widget/tableBuilder.js', function () { | ... | @@ -78,10 +77,10 @@ describe('factory: fw/widget/tableBuilder.js', function () { |
78 | }); | 77 | }); |
79 | 78 | ||
80 | it('should set tableData', function () { | 79 | it('should set tableData', function () { |
81 | - expect(mockObj.self.tableData).not.toBeDefined(); | 80 | + expect(mockObj.scope.tableData).not.toBeDefined(); |
82 | tbs.buildTable(mockObj); | 81 | tbs.buildTable(mockObj); |
83 | - expect(fs.isA(mockObj.self.tableData)).toBeTruthy(); | 82 | + expect(fs.isA(mockObj.scope.tableData)).toBeTruthy(); |
84 | - expect(mockObj.self.tableData.length).toBe(0); | 83 | + expect(mockObj.scope.tableData.length).toBe(0); |
85 | }); | 84 | }); |
86 | 85 | ||
87 | it('should unbind handlers on destroyed scope', function () { | 86 | it('should unbind handlers on destroyed scope', function () { | ... | ... |
-
Please register or login to post a comment