Bri Prebilic Cole

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
...@@ -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 -
64 - headers.each(function (d, i) {
65 - var thElement = d3.select(this),
66 - cstmWidth = thElement.attr(colWidth);
67 -
68 - if (cstmWidth) {
69 - cstmColWidth += fs.noPx(cstmWidth);
70 - } else if (thElement.classed(tableIcon)) {
71 - iconCols += 1;
72 - } else {
73 - regCols += 1;
74 - }
75 - });
76 -
77 - return Math.floor((winWidth - cstmColWidth -
78 - (iconCols * tableIconTdSize)) / regCols);
79 - }
80 59
81 - function setTableWidth(t) { 60 + function findCstmWidths() {
82 - var tHeaders = t.selectAll('th'), 61 + var headers = thead.selectAll('td');
83 - defaultColWidth = getDefaultWidth(tHeaders);
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 -
91 - if (custWidth) {
92 - setColWidth(thElement, tdElement, fs.noPx(custWidth));
93 - } else if (thElement.classed(tableIcon)) {
94 - setColWidth(thElement, tdElement, tableIconTdSize);
95 - } else {
96 - setColWidth(thElement, tdElement, defaultColWidth);
97 - }
98 - });
99 - }
100 62
101 - // get the size of the window and then subtract the extra space at the top 63 + headers.each(function (d, i) {
102 - // to get the height of the table 64 + var h = d3.select(this),
103 - function setTableHeight(thead, tbody) { 65 + index = i.toString();
104 - var ttlHgt = fs.noPxStyle(d3.select('.tabular-header'), 'height'), 66 + if (h.classed(tableIcon)) {
105 - thHgt = fs.noPxStyle(thead, 'height'), 67 + cstmWidths[index] = tableIconTdSize + 'px';
106 - totalHgt = ttlHgt + thHgt + pdg, 68 + }
107 - tbleHgt = fs.windowSize(mast.mastHeight() + totalHgt).height; 69 + if (h.attr(colWidth)) {
108 - 70 + cstmWidths[index] = h.attr(colWidth);
109 - thead.style('display', 'block'); 71 + }
110 - tbody.style({ 72 + });
111 - display: 'block', 73 + $log.debug('Headers with custom widths: ', cstmWidths);
112 - height: tbleHgt + 'px', 74 + }
113 - overflow: 'auto'
114 - });
115 - }
116 75
117 - function fixTable(t, th, tb) { 76 + function setTdWidths(elem) {
118 - setTableWidth(t); 77 + var tds = elem.selectAll('tr:not(.ignore-width)').selectAll('td');
119 - setTableHeight(th, tb); 78 + _width(elem, width + 'px');
79 +
80 + tds.each(function (d, i) {
81 + var td = d3.select(this),
82 + index = i.toString();
83 + if (cstmWidths.hasOwnProperty(index)) {
84 + _width(td, cstmWidths[index]);
85 + }
86 + });
87 + }
88 +
89 + function setHeight(body) {
90 + var h = height - (mast.mastHeight() +
91 + fs.noPxStyle(d3.select('.tabular-header'), 'height') +
92 + fs.noPxStyle(thead, 'height') + pdg);
93 + body.style('height', h + 'px');
94 + }
95 +
96 + findCstmWidths();
97 + setTdWidths(thead);
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,39 +19,44 @@ ...@@ -19,39 +19,44 @@
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
24 - onos-sortable-header
25 - sort-callback="sortCallback(requestParams)">
26 - <thead>
27 - <tr>
28 - <th colId="state" class="table-icon" sortable></th>
29 - <th colId="id" sortable>App ID </th>
30 - <th colId="version" sortable>Version </th>
31 - <th colId="origin" sortable>Origin </th>
32 - <th colId="desc" col-width="640px">Description </th>
33 - </tr>
34 - </thead>
35 23
36 - <tbody> 24 + <div class="table-header"
37 - <tr ng-hide="ctrl.tableData.length"> 25 + onos-sortable-header sort-callback="sortCallback(requestParams)">
38 - <td class="nodata" colspan="5"> 26 + <table>
39 - No Applications found 27 + <tr>
40 - </td> 28 + <td colId="state" class="table-icon" sortable></td>
41 - </tr> 29 + <td colId="id" sortable>App ID </td>
30 + <td colId="version" sortable>Version </td>
31 + <td colId="origin" sortable>Origin </td>
32 + <td colId="desc" col-width="475px">Description </td>
33 + </tr>
34 + </table>
35 + </div>
36 +
37 + <div class="table-body">
38 + <table>
39 + <tr ng-hide="tableData.length" class="no-data ignore-width">
40 + <td colspan="5">
41 + No Applications found
42 + </td>
43 + </tr>
44 +
45 + <tr ng-repeat="app in tableData"
46 + ng-click="selectCallback($event, app)"
47 + ng-class="{selected: app === sel}"
48 + ng-repeat-done>
49 + <td class="table-icon">
50 + <div icon icon-id="{{app._iconid_state}}"></div>
51 + </td>
52 + <td>{{app.id}}</td>
53 + <td>{{app.version}}</td>
54 + <td>{{app.origin}}</td>
55 + <td>{{app.desc}}</td>
56 + </tr>
57 + </table>
58 + </div>
59 +
60 + </div>
42 61
43 - <tr ng-repeat="app in ctrl.tableData"
44 - ng-click="selectCallback($event, app)"
45 - ng-class="{selected: app === sel}"
46 - ng-repeat-done>
47 - <td class="table-icon">
48 - <div icon icon-id="{{app._iconid_state}}"></div>
49 - </td>
50 - <td>{{app.id}}</td>
51 - <td>{{app.version}}</td>
52 - <td>{{app.origin}}</td>
53 - <td>{{app.desc}}</td>
54 - </tr>
55 - </tbody>
56 - </table>
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,37 +25,42 @@ ...@@ -25,37 +25,42 @@
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
30 - onos-sortable-header
31 - sort-callback="sortCallback(requestParams)">
32 - <thead>
33 - <tr>
34 - <th colId="_iconid_state" class="table-icon" sortable></th>
35 - <th colId="id" sortable>ID </th>
36 - <th colId="ip" sortable>IP Address </th>
37 - <th colId="tcp" sortable>TCP Port </th>
38 - <th colId="updated" sortable>Last Updated </th>
39 - </tr>
40 - </thead>
41 29
42 - <tbody> 30 + <div class="table-header"
43 - <tr ng-hide="ctrl.tableData.length"> 31 + onos-sortable-header sort-callback="sortCallback(requestParams)">
44 - <td class="nodata" colspan="5"> 32 + <table>
45 - No Cluster Nodes found 33 + <tr>
46 - </td> 34 + <td colId="_iconid_state" class="table-icon" sortable></td>
47 - </tr> 35 + <td colId="id" sortable>ID </td>
36 + <td colId="ip" sortable>IP Address </td>
37 + <td colId="tcp" sortable>TCP Port </td>
38 + <td colId="updated" sortable>Last Updated </td>
39 + </tr>
40 + </table>
41 + </div>
42 +
43 + <div class="table-body">
44 + <table>
45 + <tr ng-hide="tableData.length" class="no-data ignore-width">
46 + <td colspan="5">
47 + No Cluster Nodes found
48 + </td>
49 + </tr>
50 +
51 + <tr ng-repeat="node in tableData"
52 + ng-repeat-done>
53 + <td class="table-icon">
54 + <div icon icon-id="{{node._iconid_state}}"></div>
55 + </td>
56 + <td>{{node.id}}</td>
57 + <td>{{node.ip}}</td>
58 + <td>{{node.tcp}}</td>
59 + <td>{{node.updated}}</td>
60 + </tr>
61 + </table>
62 + </div>
63 +
64 + </div>
48 65
49 - <tr ng-repeat="node in ctrl.tableData"
50 - ng-repeat-done>
51 - <td class="table-icon">
52 - <div icon icon-id="{{node._iconid_state}}"></div>
53 - </td>
54 - <td>{{node.id}}</td>
55 - <td>{{node.ip}}</td>
56 - <td>{{node.tcp}}</td>
57 - <td>{{node.updated}}</td>
58 - </tr>
59 - </tbody>
60 - </table>
61 </div> 66 </div>
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
27 27
28 function ($log, $scope, ts, tbs) { 28 function ($log, $scope, ts, tbs) {
29 tbs.buildTable({ 29 tbs.buildTable({
30 - self: this,
31 scope: $scope, 30 scope: $scope,
32 tag: 'cluster' 31 tag: 'cluster'
33 }); 32 });
......
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,49 +9,54 @@ ...@@ -9,49 +9,54 @@
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
14 - onos-sortable-header
15 - sort-callback="sortCallback(requestParams)">
16 - <thead>
17 - <tr>
18 - <th colId="available" class="table-icon" sortable></th>
19 - <th colId="type" class="table-icon" sortable></th>
20 - <th colId="id" sortable>Device ID </th>
21 - <th colId="masterid" sortable>Master Instance </th>
22 - <th colId="num_ports" sortable>Ports </th>
23 - <th colId="mfr" sortable>Vendor </th>
24 - <th colId="hw" sortable>H/W Version </th>
25 - <th colId="sw" sortable>S/W Version </th>
26 - <th colId="protocol" sortable>Protocol </th>
27 - </tr>
28 - </thead>
29 13
30 - <tbody> 14 + <div class="table-header"
31 - <tr ng-hide="ctrl.tableData.length"> 15 + onos-sortable-header sort-callback="sortCallback(requestParams)">
32 - <td class="nodata" colspan="9"> 16 + <table>
33 - No Devices found 17 + <tr>
34 - </td> 18 + <td colId="available" class="table-icon" sortable></td>
35 - </tr> 19 + <td colId="type" class="table-icon" sortable></td>
20 + <td colId="id" sortable>Device ID </td>
21 + <td colId="masterid" sortable>Master Instance </td>
22 + <td colId="num_ports" sortable>Ports </td>
23 + <td colId="mfr" sortable>Vendor </td>
24 + <td colId="hw" sortable>H/W Version </td>
25 + <td colId="sw" sortable>S/W Version </td>
26 + <td colId="protocol" sortable>Protocol </td>
27 + </tr>
28 + </table>
29 + </div>
30 +
31 + <div class="table-body">
32 + <table>
33 + <tr ng-hide="tableData.length" class="no-data ignore-width">
34 + <td colspan="9">
35 + No Devices found
36 + </td>
37 + </tr>
38 +
39 + <tr ng-repeat="dev in tableData"
40 + ng-click="selectCallback($event, dev)"
41 + ng-class="{selected: dev === sel}"
42 + ng-repeat-done>
43 + <td class="table-icon">
44 + <div icon icon-id="{{dev._iconid_available}}"></div>
45 + </td>
46 + <td class="table-icon">
47 + <div icon icon-id="{{dev._iconid_type}}"></div>
48 + </td>
49 + <td>{{dev.id}}</td>
50 + <td>{{dev.masterid}}</td>
51 + <td>{{dev.num_ports}}</td>
52 + <td>{{dev.mfr}}</td>
53 + <td>{{dev.hw}}</td>
54 + <td>{{dev.sw}}</td>
55 + <td>{{dev.protocol}}</td>
56 + </tr>
57 + </table>
58 + </div>
59 +
60 + </div>
36 61
37 - <tr ng-repeat="dev in ctrl.tableData"
38 - ng-click="selectCallback($event, dev)"
39 - ng-class="{selected: dev === sel}"
40 - ng-repeat-done>
41 - <td class="table-icon">
42 - <div icon icon-id="{{dev._iconid_available}}"></div>
43 - </td>
44 - <td class="table-icon">
45 - <div icon icon-id="{{dev._iconid_type}}"></div>
46 - </td>
47 - <td>{{dev.id}}</td>
48 - <td>{{dev.masterid}}</td>
49 - <td>{{dev.num_ports}}</td>
50 - <td>{{dev.mfr}}</td>
51 - <td>{{dev.hw}}</td>
52 - <td>{{dev.sw}}</td>
53 - <td>{{dev.protocol}}</td>
54 - </tr>
55 - </tbody>
56 - </table>
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
......
...@@ -50,4 +50,5 @@ ...@@ -50,4 +50,5 @@
50 #ov-flow td.selector, 50 #ov-flow td.selector,
51 #ov-flow td.treatment { 51 #ov-flow td.treatment {
52 padding-left: 36px; 52 padding-left: 36px;
53 + opacity: 0.65;
53 } 54 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -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,46 +12,52 @@ ...@@ -12,46 +12,52 @@
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
17 - onos-sortable-header
18 - sort-callback="sortCallback(requestParams)">
19 - <thead>
20 - <tr>
21 - <th colId="id" sortable>Flow ID </th>
22 - <th colId="appId" sortable>App ID </th>
23 - <th colId="groupId" sortable>Group ID </th>
24 - <th colId="tableId" sortable>Table ID </th>
25 - <th colId="priority" sortable>Priority </th>
26 - <th colId="timeout" sortable>Timeout </th>
27 - <th colId="permanent" sortable>Permanent </th>
28 - <th colId="state" sortable>State </th>
29 - </tr>
30 - </thead>
31 16
32 - <tbody> 17 + <div class="table-header"
33 - <tr ng-hide="ctrl.tableData.length"> 18 + onos-sortable-header sort-callback="sortCallback(requestParams)">
34 - <td class="nodata" colspan="8"> 19 + <table>
35 - No Flows found 20 + <tr>
36 - </td> 21 + <td colId="id" col-width="180px" sortable>Flow ID </td>
37 - </tr> 22 + <td colId="appId" sortable>App ID </td>
23 + <td colId="groupId" sortable>Group ID </td>
24 + <td colId="tableId" sortable>Table ID </td>
25 + <td colId="priority" sortable>Priority </td>
26 + <td colId="timeout" sortable>Timeout </td>
27 + <td colId="permanent" sortable>Permanent </td>
28 + <td colId="state" sortable>State </td>
29 + </tr>
30 + </table>
31 + </div>
32 +
33 + <div class="table-body">
34 + <table>
35 + <tr ng-hide="tableData.length" class="no-data ignore-width">
36 + <td colspan="8">
37 + No Flows found
38 + </td>
39 + </tr>
40 +
41 + <tr ng-repeat-start="flow in tableData">
42 + <td>{{flow.id}}</td>
43 + <td>{{flow.appId}}</td>
44 + <td>{{flow.groupId}}</td>
45 + <td>{{flow.tableId}}</td>
46 + <td>{{flow.priority}}</td>
47 + <td>{{flow.timeout}}</td>
48 + <td>{{flow.permanent}}</td>
49 + <td>{{flow.state}}</td>
50 + </tr>
51 + <tr class="ignore-width">
52 + <td class="selector" colspan="8">{{flow.selector}}</td>
53 + </tr>
54 + <tr class="ignore-width"
55 + ng-repeat-end ng-repeat-done>
56 + <td class="treatment" colspan="8">{{flow.treatment}}</td>
57 + </tr>
58 + </table>
59 + </div>
60 +
61 + </div>
38 62
39 - <tr ng-repeat-start="flow in ctrl.tableData">
40 - <td>{{flow.id}}</td>
41 - <td>{{flow.appId}}</td>
42 - <td>{{flow.groupId}}</td>
43 - <td>{{flow.tableId}}</td>
44 - <td>{{flow.priority}}</td>
45 - <td>{{flow.timeout}}</td>
46 - <td>{{flow.permanent}}</td>
47 - <td>{{flow.state}}</td>
48 - </tr>
49 - <tr>
50 - <td class="selector" colspan="8">{{flow.selector}}</td>
51 - </tr>
52 - <tr ng-repeat-end ng-repeat-done>
53 - <td class="treatment" colspan="8">{{flow.treatment}}</td>
54 - </tr>
55 - </tbody>
56 - </table>
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,39 +9,44 @@ ...@@ -9,39 +9,44 @@
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
14 - onos-sortable-header
15 - sort-callback="sortCallback(requestParams)">
16 - <thead>
17 - <tr>
18 - <th colId="type" class="table-icon" sortable></th>
19 - <th colId="id" sortable>Host ID </th>
20 - <th colId="mac" sortable>MAC Address </th>
21 - <th colId="vlan" sortable>VLAN ID </th>
22 - <th colId="ips" sortable>IP Addresses </th>
23 - <th colId="location" sortable>Location </th>
24 - </tr>
25 - </thead>
26 13
27 - <tbody> 14 + <div class="table-header"
28 - <tr ng-hide="ctrl.tableData.length"> 15 + onos-sortable-header sort-callback="sortCallback(requestParams)">
29 - <td class="nodata" colspan="6"> 16 + <table>
30 - No Hosts found 17 + <tr>
31 - </td> 18 + <td colId="type" class="table-icon" sortable></td>
32 - </tr> 19 + <td colId="id" sortable>Host ID </td>
20 + <td colId="mac" sortable>MAC Address </td>
21 + <td colId="vlan" sortable>VLAN ID </td>
22 + <td colId="ips" sortable>IP Addresses </td>
23 + <td colId="location" sortable>Location </td>
24 + </tr>
25 + </table>
26 + </div>
27 +
28 + <div class="table-body">
29 + <table>
30 + <tr ng-hide="tableData.length" class="no-data ignore-width">
31 + <td colspan="6">
32 + No Hosts found
33 + </td>
34 + </tr>
35 +
36 + <tr ng-repeat="host in tableData"
37 + ng-repeat-done>
38 + <td class="table-icon">
39 + <div icon icon-id="{{host._iconid_type}}"></div>
40 + </td>
41 + <td>{{host.id}}</td>
42 + <td>{{host.mac}}</td>
43 + <td>{{host.vlan}}</td>
44 + <td>{{host.ips}}</td>
45 + <td>{{host.location}}</td>
46 + </tr>
47 + </table>
48 + </div>
49 +
50 + </div>
33 51
34 - <tr ng-repeat="host in ctrl.tableData"
35 - ng-repeat-done>
36 - <td class="table-icon">
37 - <div icon icon-id="{{host._iconid_type}}"></div>
38 - </td>
39 - <td>{{host.id}}</td>
40 - <td>{{host.mac}}</td>
41 - <td>{{host.vlan}}</td>
42 - <td>{{host.ips}}</td>
43 - <td>{{host.location}}</td>
44 - </tr>
45 - </tbody>
46 - </table>
47 </div> 52 </div>
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
27 27
28 function ($log, $scope, ts, tbs) { 28 function ($log, $scope, ts, tbs) {
29 tbs.buildTable({ 29 tbs.buildTable({
30 - self: this,
31 scope: $scope, 30 scope: $scope,
32 tag: 'host' 31 tag: 'host'
33 }); 32 });
...@@ -37,7 +36,7 @@ ...@@ -37,7 +36,7 @@
37 ts.resetSortIcons(); 36 ts.resetSortIcons();
38 $scope.sortCallback(); 37 $scope.sortCallback();
39 }; 38 };
40 - 39 +
41 $log.log('OvHostCtrl has been created'); 40 $log.log('OvHostCtrl has been created');
42 }]); 41 }]);
43 }()); 42 }());
......
...@@ -50,4 +50,5 @@ ...@@ -50,4 +50,5 @@
50 #ov-intent td.resources, 50 #ov-intent td.resources,
51 #ov-intent td.details { 51 #ov-intent td.details {
52 padding-left: 36px; 52 padding-left: 36px;
53 + opacity: 0.65;
53 } 54 }
......
...@@ -17,45 +17,51 @@ ...@@ -17,45 +17,51 @@
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"
28 - onos-fixed-header
29 - onos-sortable-header
30 - sort-callback="sortCallback(requestParams)">
31 - <thead>
32 - <tr>
33 - <th colId="appId" sortable>Application ID </th>
34 - <th colId="key" sortable>Key </th>
35 - <th colId="type" sortable>Type </th>
36 - <th colId="priority" sortable>Priority </th>
37 - </tr>
38 - </thead>
39 27
40 - <tbody> 28 + <div class="summary-list" onos-fixed-header>
41 - <tr ng-hide="ctrl.tableData.length"> 29 +
42 - <td class="nodata" colspan="4"> 30 + <div class="table-header"
43 - No Intents found 31 + onos-sortable-header sort-callback="sortCallback(requestParams)">
44 - </td> 32 + <table>
45 - </tr> 33 + <tr>
34 + <td colId="appId" sortable>Application ID </td>
35 + <td colId="key" sortable>Key </td>
36 + <td colId="type" sortable>Type </td>
37 + <td colId="priority" sortable>Priority </td>
38 + </tr>
39 + </table>
40 + </div>
41 +
42 + <div class="table-body">
43 + <table>
44 + <tr ng-hide="tableData.length" class="no-data ignore-width">
45 + <td colspan="4">
46 + No Intents found
47 + </td>
48 + </tr>
49 +
50 + <tr ng-repeat-start="intent in tableData">
51 + <td>{{intent.appId}}</td>
52 + <td>{{intent.key}}</td>
53 + <td>{{intent.type}}</td>
54 + <td>{{intent.priority}}</td>
55 + </tr>
56 + <tr>
57 + <td class="resources" colspan="4">{{intent.resources}}</td>
58 + </tr>
59 + <tr ng-repeat-end ng-repeat-done>
60 + <td class="details" colspan="4">{{intent.details}}</td>
61 + </tr>
62 + </table>
63 + </div>
64 +
65 + </div>
46 66
47 - <tr ng-repeat-start="intent in ctrl.tableData">
48 - <td>{{intent.appId}}</td>
49 - <td>{{intent.key}}</td>
50 - <td>{{intent.type}}</td>
51 - <td>{{intent.priority}}</td>
52 - </tr>
53 - <tr>
54 - <td class="resources" colspan="4">{{intent.resources}}</td>
55 - </tr>
56 - <tr ng-repeat-end ng-repeat-done>
57 - <td class="details" colspan="4">{{intent.details}}</td>
58 - </tr>
59 - </tbody>
60 - </table>
61 </div> 67 </div>
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
27 27
28 function ($log, $scope, ts, tbs) { 28 function ($log, $scope, ts, tbs) {
29 tbs.buildTable({ 29 tbs.buildTable({
30 - self: this,
31 scope: $scope, 30 scope: $scope,
32 tag: 'intent' 31 tag: 'intent'
33 }); 32 });
......
...@@ -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,39 +25,44 @@ ...@@ -25,39 +25,44 @@
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
30 - onos-sortable-header
31 - sort-callback="sortCallback(requestParams)">
32 - <thead>
33 - <tr>
34 - <th colId="_iconid_state" class="table-icon" sortable></th>
35 - <th colId="one" sortable>Port 1 </th>
36 - <th colId="two" sortable>Port 2 </th>
37 - <th colId="type" sortable>Type </th>
38 - <th colId="direction" sortable>Direction </th>
39 - <th colId="durable" sortable>Durable </th>
40 - </tr>
41 - </thead>
42 29
43 - <tbody> 30 + <div class="table-header"
44 - <tr ng-hide="ctrl.tableData.length"> 31 + onos-sortable-header sort-callback="sortCallback(requestParams)">
45 - <td class="nodata" colspan="6"> 32 + <table>
46 - No Links found 33 + <tr>
47 - </td> 34 + <td colId="_iconid_state" class="table-icon" sortable></td>
48 - </tr> 35 + <td colId="one" sortable>Port 1 </td>
36 + <td colId="two" sortable>Port 2 </td>
37 + <td colId="type" sortable>Type </td>
38 + <td colId="direction" sortable>Direction </td>
39 + <td colId="durable" sortable>Durable </td>
40 + </tr>
41 + </table>
42 + </div>
43 +
44 + <div class="table-body">
45 + <table>
46 + <tr ng-hide="tableData.length" class="no-data ignore-width">
47 + <td colspan="6">
48 + No Links found
49 + </td>
50 + </tr>
51 +
52 + <tr ng-repeat="link in tableData"
53 + ng-repeat-done>
54 + <td class="table-icon">
55 + <div icon icon-id="{{link._iconid_state}}"></div>
56 + </td>
57 + <td>{{link.one}}</td>
58 + <td>{{link.two}}</td>
59 + <td>{{link.type}}</td>
60 + <td>{{link.direction}}</td>
61 + <td>{{link.durable}}</td>
62 + </tr>
63 + </table>
64 + </div>
65 +
66 + </div>
49 67
50 - <tr ng-repeat="link in ctrl.tableData"
51 - ng-repeat-done>
52 - <td class="table-icon">
53 - <div icon icon-id="{{link._iconid_state}}"></div>
54 - </td>
55 - <td>{{link.one}}</td>
56 - <td>{{link.two}}</td>
57 - <td>{{link.type}}</td>
58 - <td>{{link.direction}}</td>
59 - <td>{{link.durable}}</td>
60 - </tr>
61 - </tbody>
62 - </table>
63 </div> 68 </div>
......
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
27 27
28 function ($log, $scope, ts, tbs) { 28 function ($log, $scope, ts, tbs) {
29 tbs.buildTable({ 29 tbs.buildTable({
30 - self: this,
31 scope: $scope, 30 scope: $scope,
32 tag: 'link' 31 tag: 'link'
33 }); 32 });
...@@ -37,7 +36,7 @@ ...@@ -37,7 +36,7 @@
37 ts.resetSortIcons(); 36 ts.resetSortIcons();
38 $scope.sortCallback(); 37 $scope.sortCallback();
39 }; 38 };
40 - 39 +
41 $log.log('OvLinkCtrl has been created'); 40 $log.log('OvLinkCtrl has been created');
42 }]); 41 }]);
43 }()); 42 }());
......
...@@ -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">' +
34 - '<tr>' + 33 + '<table>' +
35 - '<th></th>' + 34 + '<tr>' +
36 - '<th>Device ID </th>' + 35 + '<td colId="type" class="table-icon"></td>' +
37 - '<th col-width="100px">H/W Version </th>' + 36 + '<td colId="id">Host ID </td>' +
38 - '<th>S/W Version </th>' + 37 + '<td colId="mac" sortable>MAC Address </td>' +
39 - '</tr>' + 38 + '<td colId="location" col-width="110px">Location </td>' +
40 - '</thead>' + 39 + '</tr>' +
41 - '<tbody>' + 40 + '</table>' +
42 - '<tr>' + 41 + '</div>' +
43 - '<td colspan="4">' + 42 +
44 - 'No Devices found' + 43 + '<div class="table-body">' +
45 - '</td>' + 44 + '<table>' +
46 - '</tr>' + 45 + '<tr class="ignore-width">' +
47 - '<tr>' + 46 + '<td class="not-picked"></td>' +
48 - '<td class="table-icon">' + 47 + '</tr>' +
49 - '<div icon icon-id="{{dev._iconid_available}}">' + 48 + '<tr>' +
50 - '</div>' + 49 + '<td class="table-icon">Some Icon</td>' +
51 - '</td>' + 50 + '<td>Some ID</td>' +
52 - '<td>Some ID</td>' + 51 + '<td>Some MAC Address</td>' +
53 - '<td>Some HW</td>' + 52 + '<td>Some Location</td>' +
54 - '<td>Some Software</td>' + 53 + '</tr>' +
55 - '</tr>' + 54 + '</table>' +
56 - '</tbody>' + 55 + '</div>' +
57 - '</table>', 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);
254 301
255 - compileTable(); 302 + verifyGivenTags('onos-fixed-header', containerDiv);
256 - verifyGivenTags('onos-fixed-header'); 303 + selectTables();
304 + verifyDefaultSize();
305 +
306 + populateTableData();
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 () {
......