Bri Prebilic Cole

GUI -- Allows for col-width="_px" to be specified in the html of table headers.

- Refactored table.js code
- Added helper functions to FnService.
- Deleted "sortable" from html in columns where sorting doesn't make sense (icons).
- Updated unit tests to reflect changes

Change-Id: I425101071bd5c7f237d64d98084a726cfce1d016
...@@ -150,6 +150,7 @@ ...@@ -150,6 +150,7 @@
150 return found; 150 return found;
151 } 151 }
152 152
153 + // return true if the object is empty, return false otherwise
153 function isEmptyObject(obj) { 154 function isEmptyObject(obj) {
154 var key; 155 var key;
155 for (key in obj) { 156 for (key in obj) {
...@@ -165,6 +166,16 @@ ...@@ -165,6 +166,16 @@
165 }); 166 });
166 } 167 }
167 168
169 + // return the parameter without a px suffix
170 + function noPx(num) {
171 + return Number(num.replace(/px$/, ''));
172 + }
173 +
174 + // return an element's given style property without px suffix
175 + function noPxStyle(elem, prop) {
176 + return Number(elem.style(prop).replace(/px$/, ''));
177 + }
178 +
168 angular.module('onosUtil') 179 angular.module('onosUtil')
169 .factory('FnService', ['$window', function (_$window_) { 180 .factory('FnService', ['$window', function (_$window_) {
170 $window = _$window_; 181 $window = _$window_;
...@@ -183,7 +194,9 @@ ...@@ -183,7 +194,9 @@
183 inArray: inArray, 194 inArray: inArray,
184 removeFromArray: removeFromArray, 195 removeFromArray: removeFromArray,
185 isEmptyObject: isEmptyObject, 196 isEmptyObject: isEmptyObject,
186 - cap: cap 197 + cap: cap,
198 + noPx: noPx,
199 + noPxStyle: noPxStyle
187 }; 200 };
188 }]); 201 }]);
189 202
......
...@@ -20,45 +20,74 @@ ...@@ -20,45 +20,74 @@
20 (function () { 20 (function () {
21 'use strict'; 21 'use strict';
22 22
23 - var $log, $window, fs, is, 23 + // injected refs
24 - currCol = {}, 24 + var $log, $window, fs, is;
25 - prevCol = {},
26 - tableIconTdSize = 33,
27 - bottomMargin = 200;
28 25
29 - // Functions for creating a fixed header on a table (Angular Directive) 26 + // constants
27 + var tableIconTdSize = 33,
28 + bottomMargin = 200,
29 + colWidth = 'col-width',
30 + tableIcon = 'table-icon';
30 31
31 - function setTableWidth(t) { 32 + // internal state
32 - var tHeaders, tdElement, colWidth, numIcons, numNonIcons, 33 + var currCol = {},
33 - winWidth = fs.windowSize().width; 34 + prevCol = {};
34 35
35 - tHeaders = t.selectAll('th'); 36 + // Functions for creating a fixed header on a table (Angular Directive)
36 - numIcons = 0;
37 - numNonIcons = 0;
38 37
39 - // FIXME: This should observe custom-set width from the HTML 38 + function setElemWidth(elem, size) {
39 + elem.style('width', size + 'px')
40 + }
40 41
41 - tHeaders.each(function(thElement, index) { 42 + function setColWidth(th, td, size) {
42 - thElement = d3.select(this); 43 + setElemWidth(th, size);
43 - if (thElement.classed('table-icon')) { 44 + setElemWidth(td, size);
44 - numIcons = numIcons + 1; 45 + }
46 +
47 + // count number of headers of
48 + // - assigned width,
49 + // - icon width,
50 + // - and default width
51 + // assumes assigned width is not given to icons
52 + // returns the width of all columns that are not icons have an assigned width
53 + function getDefaultWidth(headers) {
54 + var winWidth = fs.windowSize().width,
55 + iconCols = 0,
56 + regCols = 0,
57 + cstmColWidth = 0;
58 +
59 + headers.each(function (d, i) {
60 + var thElement = d3.select(this),
61 + cstmWidth = thElement.attr(colWidth);
62 +
63 + if (cstmWidth) {
64 + cstmColWidth += fs.noPx(cstmWidth);
65 + } else if (thElement.classed(tableIcon)) {
66 + iconCols += 1;
45 } else { 67 } else {
46 - numNonIcons = numNonIcons + 1; 68 + regCols += 1;
47 } 69 }
48 }); 70 });
49 71
50 - colWidth = Math.floor((winWidth - (numIcons * tableIconTdSize)) / numNonIcons); 72 + return Math.floor((winWidth - cstmColWidth -
51 - 73 + (iconCols * tableIconTdSize)) / regCols);
52 - tHeaders.each(function(thElement, index) { 74 + }
53 - thElement = d3.select(this);
54 - tdElement = t.select('td:nth-of-type(' + (index + 1) + ')');
55 75
56 - if (thElement.classed('table-icon')) { 76 + function setTableWidth(t) {
57 - thElement.style('width', tableIconTdSize + 'px'); 77 + var tHeaders = t.selectAll('th'),
58 - tdElement.style('width', tableIconTdSize + 'px'); 78 + defaultColWidth = getDefaultWidth(tHeaders);
79 +
80 + tHeaders.each(function (d, i) {
81 + var thElement = d3.select(this),
82 + tdElement = t.select('td:nth-of-type(' + (i + 1) + ')'),
83 + custWidth = thElement.attr(colWidth);
84 +
85 + if (custWidth) {
86 + setColWidth(thElement, tdElement, fs.noPx(custWidth));
87 + } else if (thElement.classed(tableIcon)) {
88 + setColWidth(thElement, tdElement, tableIconTdSize);
59 } else { 89 } else {
60 - thElement.style('width', colWidth + 'px'); 90 + setColWidth(thElement, tdElement, defaultColWidth);
61 - tdElement.style('width', colWidth + 'px');
62 } 91 }
63 }); 92 });
64 } 93 }
......
...@@ -75,11 +75,6 @@ ...@@ -75,11 +75,6 @@
75 return null; 75 return null;
76 } 76 }
77 77
78 - function noPxWidth(elem) {
79 - return Number(elem.style('width').replace(/px$/, ''));
80 - }
81 -
82 -
83 // ================================== 78 // ==================================
84 79
85 function createToolbar(id, opts) { 80 function createToolbar(id, opts) {
...@@ -119,7 +114,7 @@ ...@@ -119,7 +114,7 @@
119 } 114 }
120 115
121 function adjustWidth(btnWidth) { 116 function adjustWidth(btnWidth) {
122 - if (noPxWidth(currentRow) >= maxWidth) { 117 + if (fs.noPxStyle(currentRow, 'width') >= maxWidth) {
123 tbWidth += btnWidth; 118 tbWidth += btnWidth;
124 maxWidth = tbWidth; 119 maxWidth = tbWidth;
125 } 120 }
......
...@@ -213,7 +213,8 @@ describe('factory: fw/util/fn.js', function() { ...@@ -213,7 +213,8 @@ describe('factory: fw/util/fn.js', function() {
213 expect(fs.areFunctions(fs, [ 213 expect(fs.areFunctions(fs, [
214 'isF', 'isA', 'isS', 'isO', 'contains', 214 'isF', 'isA', 'isS', 'isO', 'contains',
215 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'isMobile', 215 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'isMobile',
216 - 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'cap' 216 + 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'cap',
217 + 'noPx', 'noPxStyle'
217 ])).toBeTruthy(); 218 ])).toBeTruthy();
218 }); 219 });
219 220
...@@ -387,4 +388,27 @@ describe('factory: fw/util/fn.js', function() { ...@@ -387,4 +388,27 @@ describe('factory: fw/util/fn.js', function() {
387 expect(fs.cap('foo bar')).toEqual('Foo bar'); 388 expect(fs.cap('foo bar')).toEqual('Foo bar');
388 }); 389 });
389 390
391 + // === Tests for noPx()
392 + it('should return the value without px suffix', function () {
393 + expect(fs.noPx('10px')).toBe(10);
394 + expect(fs.noPx('500px')).toBe(500);
395 + expect(fs.noPx('-80px')).toBe(-80);
396 + });
397 +
398 + // === Tests for noPxStyle()
399 + it("should give a style's property without px suffix", function () {
400 + var d3Elem = d3.select('body')
401 + .append('div')
402 + .attr('id', 'fooElem')
403 + .style({
404 + width: '500px',
405 + height: '200px',
406 + 'font-size': '12px'
407 + });
408 + expect(fs.noPxStyle(d3Elem, 'width')).toBe(500);
409 + expect(fs.noPxStyle(d3Elem, 'height')).toBe(200);
410 + expect(fs.noPxStyle(d3Elem, 'font-size')).toBe(12);
411 + d3.select('#fooElem').remove();
412 + });
413 +
390 }); 414 });
......
...@@ -32,7 +32,7 @@ describe('factory: fw/widget/table.js', function () { ...@@ -32,7 +32,7 @@ describe('factory: fw/widget/table.js', function () {
32 '<tr>' + 32 '<tr>' +
33 '<th></th>' + 33 '<th></th>' +
34 '<th>Device ID </th>' + 34 '<th>Device ID </th>' +
35 - '<th>H/W Version </th>' + 35 + '<th col-width="100px">H/W Version </th>' +
36 '<th>S/W Version </th>' + 36 '<th>S/W Version </th>' +
37 '</tr>' + 37 '</tr>' +
38 '</thead>' + 38 '</thead>' +
...@@ -51,7 +51,7 @@ describe('factory: fw/widget/table.js', function () { ...@@ -51,7 +51,7 @@ describe('factory: fw/widget/table.js', function () {
51 51
52 onosSortableHeaderTags = '<table ' + 52 onosSortableHeaderTags = '<table ' +
53 'onos-sortable-header ' + 53 'onos-sortable-header ' +
54 - 'sort-callback="sortCallback(urlSuffix)">' + 54 + 'sort-callback="sortCallback(requestParams)">' +
55 '<thead>' + 55 '<thead>' +
56 '<tr>' + 56 '<tr>' +
57 '<th colId="available"></th>' + 57 '<th colId="available"></th>' +
...@@ -146,8 +146,12 @@ describe('factory: fw/widget/table.js', function () { ...@@ -146,8 +146,12 @@ describe('factory: fw/widget/table.js', function () {
146 angular.forEach(thElems, function (thElem, i) { 146 angular.forEach(thElems, function (thElem, i) {
147 thElem = angular.element(thElems[i]); 147 thElem = angular.element(thElems[i]);
148 tdElem = angular.element(tbody.find('td').eq(i)); 148 tdElem = angular.element(tbody.find('td').eq(i));
149 + var custWidth = thElem.attr('col-width');
149 150
150 - if (tdElem.attr('class') === 'table-icon') { 151 + if (custWidth) {
152 + expect(thElem.css('width')).toBe(custWidth);
153 + expect(tdElem.css('width')).toBe(custWidth);
154 + } else if (tdElem.attr('class') === 'table-icon') {
151 expect(thElem.css('width')).toBe(tableIconTdSize + 'px'); 155 expect(thElem.css('width')).toBe(tableIconTdSize + 'px');
152 expect(tdElem.css('width')).toBe(tableIconTdSize + 'px'); 156 expect(tdElem.css('width')).toBe(tableIconTdSize + 'px');
153 } else { 157 } else {
......