[Table] #798 Simplified markup, moved styles to external stylesheet

This commit is contained in:
Henry 2016-04-03 14:25:09 -07:00
parent c591ade479
commit 23a8c305c1
5 changed files with 148 additions and 80 deletions

View File

@ -161,6 +161,12 @@ define([
"key": "table-options-edit",
"templateUrl": "templates/table-options-edit.html"
}
],
"stylesheets": [
{
"stylesheetUrl": "css/table.css",
"priority": "mandatory"
}
]
}
});

View File

@ -0,0 +1,50 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.sizing-table {
min-width: 100%;
z-index: -1;
visibility: hidden;
position: absolute;
//Add some padding to allow for decorations such as limits indicator
td {
padding-right: 15px;
padding-left: 10px;
white-space: nowrap;
}
}
.mct-table {
table-layout: fixed;
th {
box-sizing: border-box;
}
tbody {
tr {
position: absolute;
}
td {
white-space: nowrap;
overflow: hidden;
box-sizing: border-box;
}
}
}

View File

@ -1,22 +1,25 @@
<div class="l-view-section scrolling"
ng-style="overrideRowPositioning ?
{'overflow': 'auto'} :
{'overflow': 'scroll'}"
>
<table class="filterable"
ng-style="overrideRowPositioning && {
<div class="l-view-section scrolling" style="overflow: auto;">
<table class="sizing-table">
<tbody>
<tr>
<td ng-repeat="header in displayHeaders">{{header}}</td>
</tr>
<tr><td ng-repeat="header in displayHeaders" >
{{sizingRow[header].text}}
</td></tr>
</tbody>
</table>
<table class="filterable mct-table"
ng-style="{
height: totalHeight + 'px',
'table-layout': overrideRowPositioning ? 'fixed' : 'auto',
'max-width': totalWidth
}">
}">
<thead>
<tr>
<th ng-repeat="header in displayHeaders"
ng-style="overrideRowPositioning && {
ng-style="{
width: columnWidths[$index] + 'px',
'max-width': columnWidths[$index] + 'px',
overflow: 'none',
'box-sizing': 'border-box'
}"
ng-class="[
enableSort ? 'sortable' : '',
@ -29,11 +32,9 @@
</tr>
<tr ng-if="enableFilter" class="s-filters">
<th ng-repeat="header in displayHeaders"
ng-style="overrideRowPositioning && {
ng-style="{
width: columnWidths[$index] + 'px',
'max-width': columnWidths[$index] + 'px',
overflow: 'none',
'box-sizing': 'border-box'
}">
<input type="text"
ng-model="filters[header]"/>
@ -41,21 +42,15 @@
</tr>
</thead>
<tbody ng-style="overrideRowPositioning ? '' : {
'opacity': '0.0'
}">
<tbody>
<tr ng-repeat="visibleRow in visibleRows track by visibleRow.rowIndex"
ng-style="overrideRowPositioning && {
position: 'absolute',
ng-style="{
top: visibleRow.offsetY + 'px',
}">
<td ng-repeat="header in displayHeaders"
ng-style="overrideRowPositioning && {
ng-style=" {
width: columnWidths[$index] + 'px',
'white-space': 'nowrap',
'max-width': columnWidths[$index] + 'px',
overflow: 'hidden',
'box-sizing': 'border-box'
}"
class="{{visibleRow.contents[header].cssClass}}">
{{ visibleRow.contents[header].text }}

View File

@ -23,10 +23,13 @@ define(
this.maxDisplayRows = 50;
this.scrollable = element.find('div');
this.thead = element.find('thead');
this.tbody = element.find('tbody');
this.$scope.sizingRow = {};
this.scrollable.on('scroll', this.onScroll.bind(this));
$scope.visibleRows = [];
$scope.overrideRowPositioning = false;
/**
* Set default values for optional parameters on a given scope
@ -100,14 +103,31 @@ define(
* @private
*/
MCTTableController.prototype.newRow = function (event, rowIndex) {
var row = this.$scope.rows[rowIndex];
//Add row to the filtered, sorted list of all rows
if (this.filterRows([row]).length > 0) {
this.insertSorted(this.$scope.displayRows, row);
var self = this,
row = this.$scope.rows[rowIndex],
largestRow;
function sizeAndScroll () {
self.setElementSizes();
self.scrollToBottom();
}
this.$timeout(this.setElementSizes.bind(this))
.then(this.scrollToBottom.bind(this));
//Does the row pass the current filter?
if (this.filterRows([row]).length === 1) {
this.insertSorted(this.$scope.displayRows, row);
//Calculate largest row
largestRow = this.buildLargestRow([this.$scope.sizingRow, row]);
// Has it changed? If so, set the the 'sizing' row which
// determines column widths
if (JSON.stringify(largestRow) !== JSON.stringify(this.$scope.sizingRow)){
this.$scope.sizingRow = largestRow;
this.$timeout(sizeAndScroll);
} else {
sizeAndScroll();
}
}
};
/**
@ -249,13 +269,12 @@ define(
* for individual rows.
*/
MCTTableController.prototype.setElementSizes = function () {
var self = this,
thead = this.element.find('thead'),
tbody = this.element.find('tbody'),
var thead = this.thead,
tbody = this.tbody,
firstRow = tbody.find('tr'),
column = firstRow.find('td'),
headerHeight = thead.prop('offsetHeight'),
rowHeight = 20,
rowHeight = firstRow.prop('offsetHeight'),
columnWidth,
tableWidth = 0,
overallHeight = headerHeight + (rowHeight *
@ -279,8 +298,6 @@ define(
} else {
this.$scope.totalWidth = 'none';
}
this.$scope.overrideRowPositioning = true;
};
/**
@ -400,43 +417,32 @@ define(
* pre-calculate optimal column sizes without having to render
* every row.
*/
MCTTableController.prototype.findLargestRow = function (rows) {
var largestRow = rows.reduce(function (largestRow, row) {
MCTTableController.prototype.buildLargestRow = function (rows) {
var largestRow = rows.reduce(function (prevLargest, row) {
Object.keys(row).forEach(function (key) {
var currentColumn = row[key].text,
var currentColumn,
currentColumnLength,
largestColumn,
largestColumnLength;
if (!row[key]){
//do nothing, no value for this column;
} else {
currentColumn = (row[key]).text;
currentColumnLength =
(currentColumn && currentColumn.length) ?
currentColumn.length :
currentColumn,
largestColumn = largestRow[key].text,
largestColumnLength =
(largestColumn && largestColumn.length) ?
largestColumn.length :
largestColumn;
currentColumn;
largestColumn = prevLargest[key] ? prevLargest[key].text : "";
largestColumnLength = largestColumn.length;
if (currentColumnLength > largestColumnLength) {
largestRow[key] = JSON.parse(JSON.stringify(row[key]));
if (currentColumnLength > largestColumnLength) {
prevLargest[key] = JSON.parse(JSON.stringify(row[key]));
}
}
});
return largestRow;
return prevLargest;
}, JSON.parse(JSON.stringify(rows[0] || {})));
largestRow = JSON.parse(JSON.stringify(largestRow));
// Pad with characters to accomodate variable-width fonts,
// and remove characters that would allow word-wrapping.
Object.keys(largestRow).forEach(function (key) {
var padCharacters,
i;
largestRow[key].text = String(largestRow[key].text);
padCharacters = largestRow[key].text.length / 10;
for (i = 0; i < padCharacters; i++) {
largestRow[key].text = largestRow[key].text + 'W';
}
largestRow[key].text = largestRow[key].text
.replace(/[ \-_]/g, 'W');
});
return largestRow;
};
@ -447,20 +453,13 @@ define(
* @private
*/
MCTTableController.prototype.resize = function (){
var largestRow = this.findLargestRow(this.$scope.displayRows),
self = this;
this.$scope.visibleRows = [
{
rowIndex: 0,
offsetY: undefined,
contents: largestRow
}
];
var self = this;
this.$scope.sizingRow = this.buildLargestRow(this.$scope.displayRows);
//Wait a timeout to allow digest of previous change to visible
// rows to happen.
this.$timeout(function () {
//Remove temporary padding row used for setting column widths
self.$scope.visibleRows = [];
self.setElementSizes();
});
@ -489,8 +488,6 @@ define(
//Reset visible rows because new row data available.
this.$scope.visibleRows = [];
this.$scope.overrideRowPositioning = false;
//Nothing to show because no columns visible
if (!this.$scope.displayHeaders) {
return;

View File

@ -58,15 +58,18 @@ define(
mockElement = jasmine.createSpyObj('element', [
'find',
'prop',
'on'
]);
mockElement.find.andReturn(mockElement);
mockElement.prop.andReturn(0);
mockScope.displayHeaders = true;
mockTimeout = jasmine.createSpy('$timeout');
mockTimeout.andReturn(promise(undefined));
controller = new MCTTableController(mockScope, mockTimeout, mockElement);
spyOn(controller, 'setVisibleRows');
});
it('Reacts to changes to filters, headers, and rows', function() {
@ -138,8 +141,6 @@ define(
var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length-1].args[1];
controller.updateRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
spyOn(controller, 'setVisibleRows');
//controller.setVisibleRows.andReturn(undefined);
removeRowFunc(undefined, 2);
expect(mockScope.displayRows.length).toBe(2);
expect(controller.setVisibleRows).toHaveBeenCalled();
@ -266,6 +267,25 @@ define(
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
});
it('Resizes columns if length of any columns in new' +
' row exceeds corresponding existing column', function() {
var row7 = {
'col1': {'text': 'row6 col1'},
'col2': {'text': 'some longer string'},
'col3': {'text': 'row6 col3'}
};
mockScope.sortColumn = undefined;
mockScope.sortDirection = undefined;
mockScope.filters = {};
mockScope.displayRows = testRows.slice(0);
mockScope.rows.push(row7);
controller.newRow(undefined, mockScope.rows.length-1);
expect(controller.$scope.sizingRow.col2).toEqual({text: 'some longer string'});
});
});
});