diff --git a/src/traces/table/constants.js b/src/traces/table/constants.js
index cf24a0f2110..2ffde16af90 100644
--- a/src/traces/table/constants.js
+++ b/src/traces/table/constants.js
@@ -9,26 +9,27 @@
'use strict';
module.exports = {
- maxDimensionCount: 60,
- overdrag: 45,
cellPad: 8,
+ columnExtentOffset: 10,
+ columnTitleOffset: 28,
+ emptyHeaderHeight: 16,
latexCheck: /^\$.*\$$/,
- wrapSplitCharacter: ' ',
- wrapSpacer: ' ',
- lineBreaker: '
',
- uplift: 5,
goldenRatio: 1.618,
- columnTitleOffset: 28,
- columnExtentOffset: 10,
- transitionEase: 'cubic-out',
- transitionDuration: 100,
- releaseTransitionEase: 'cubic-out',
+ lineBreaker: '
',
+ maxDimensionCount: 60,
+ overdrag: 45,
releaseTransitionDuration: 120,
- scrollbarWidth: 8,
+ releaseTransitionEase: 'cubic-out',
scrollbarCaptureWidth: 18,
- scrollbarOffset: 5,
scrollbarHideDelay: 1000,
scrollbarHideDuration: 1000,
+ scrollbarOffset: 5,
+ scrollbarWidth: 8,
+ transitionDuration: 100,
+ transitionEase: 'cubic-out',
+ uplift: 5,
+ wrapSpacer: ' ',
+ wrapSplitCharacter: ' ',
cn: {
// general class names
table: 'table',
diff --git a/src/traces/table/data_preparation_helper.js b/src/traces/table/data_preparation_helper.js
index 25ca7ca5387..9e91767231c 100644
--- a/src/traces/table/data_preparation_helper.js
+++ b/src/traces/table/data_preparation_helper.js
@@ -14,14 +14,17 @@ var isNumeric = require('fast-isnumeric');
// pure functions, don't alter but passes on `gd` and parts of `trace` without deep copying
module.exports = function calc(gd, trace) {
+ var cellsValues = trace.cells.values;
+ var slicer = function(a) {
+ return a.slice(trace.header.values.length, a.length);
+ };
var headerValues = trace.header.values.map(function(c) {
return Array.isArray(c) ? c : [c];
- });
- var cellsValues = trace.cells.values;
+ }).concat(slicer(cellsValues).map(function() {return [''];}));
var domain = trace.domain;
var groupWidth = Math.floor(gd._fullLayout._size.w * (domain.x[1] - domain.x[0]));
var groupHeight = Math.floor(gd._fullLayout._size.h * (domain.y[1] - domain.y[0]));
- var headerRowHeights = headerValues.length ? headerValues[0].map(function() {return trace.header.height;}) : [];
+ var headerRowHeights = trace.header.values.length ? headerValues[0].map(function() {return trace.header.height;}) : [c.emptyHeaderHeight];
var rowHeights = cellsValues.length ? cellsValues[0].map(function() {return trace.cells.height;}) : [];
var headerHeight = headerRowHeights.reduce(function(a, b) {return a + b;}, 0);
var scrollHeight = groupHeight - headerHeight;
@@ -31,7 +34,7 @@ module.exports = function calc(gd, trace) {
var headerRowBlocks = makeRowBlock(anchorToHeaderRowBlock, []);
var rowBlocks = makeRowBlock(anchorToRowBlock, headerRowBlocks);
var uniqueKeys = {};
- var columnOrder = trace._fullInput.columnorder;
+ var columnOrder = trace._fullInput.columnorder.concat(slicer(cellsValues.map(function(d, i) {return i;})));
var columnWidths = headerValues.map(function(d, i) {
var value = Array.isArray(trace.columnwidth) ?
trace.columnwidth[Math.min(i, trace.columnwidth.length - 1)] :
diff --git a/src/traces/table/data_split_helpers.js b/src/traces/table/data_split_helpers.js
index 3f62666cc49..7ea90d4c07d 100644
--- a/src/traces/table/data_split_helpers.js
+++ b/src/traces/table/data_split_helpers.js
@@ -51,7 +51,7 @@ exports.splitToPanels = function(d) {
exports.splitToCells = function(d) {
var fromTo = rowFromTo(d);
- return d.values.slice(fromTo[0], fromTo[1]).map(function(v, i) {
+ return (d.values || []).slice(fromTo[0], fromTo[1]).map(function(v, i) {
// By keeping identical key, a DOM node removal, creation and addition is spared, important when visible
// grid has a lot of elements (quadratic with xcol/ycol count).
// But it has to be busted when `svgUtil.convertToTspans` is used as it reshapes cell subtrees asynchronously,
diff --git a/test/jasmine/tests/table_test.js b/test/jasmine/tests/table_test.js
index 5e599cd593e..2d532275963 100644
--- a/test/jasmine/tests/table_test.js
+++ b/test/jasmine/tests/table_test.js
@@ -206,6 +206,57 @@ describe('table', function() {
});
});
+ describe('Rendering with partial attribute support', function() {
+ var mockCopy,
+ gd;
+
+ afterEach(destroyGraphDiv);
+
+ it('`Plotly.plot` should render all the columns even if no cell contents were supplied yet', function(done) {
+ gd = createGraphDiv();
+ mockCopy = Lib.extendDeep({}, mock);
+ delete mockCopy.data[0].cells;
+ Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
+ expect(gd.data.length).toEqual(1);
+ expect(gd.data[0].header.values.length).toEqual(7);
+ expect(document.querySelectorAll('.' + cn.yColumn).length).toEqual(7);
+ expect(document.querySelectorAll('.' + cn.columnCell).length).toEqual(7 * 2); // both column rows to render
+ done();
+ });
+ });
+
+ it('`Plotly.plot` should render all columns even if no header contents were supplied yet', function(done) {
+ gd = createGraphDiv();
+ mockCopy = Lib.extendDeep({}, mock);
+ delete mockCopy.data[0].header;
+ Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
+ expect(gd.data.length).toEqual(1);
+ expect(gd.data[0].cells.values.length).toEqual(7);
+ expect(document.querySelectorAll('.' + cn.yColumn).length).toEqual(7);
+ expect(document.querySelectorAll('.' + cn.columnCell).length).toEqual(7 * 29);
+ expect(document.querySelectorAll('#header').length).toEqual(7);
+ expect(document.querySelectorAll('#header .' + cn.columnCell).length).toEqual(7);
+ expect(document.querySelector('#header .' + cn.columnCell + ' text').textContent).toEqual('');
+ done();
+ });
+ });
+
+ it('`Plotly.plot` should render all the column headers even if not all header values were supplied', function(done) {
+ gd = createGraphDiv();
+ mockCopy = Lib.extendDeep({}, mock);
+ mockCopy.data[0].header.values = ['A', 'S', 'D']; // doesn't cover all 7 columns
+ Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
+ expect(gd.data.length).toEqual(1);
+ expect(gd.data[0].cells.values.length).toEqual(7);
+ expect(document.querySelectorAll('.' + cn.yColumn).length).toEqual(7);
+ expect(document.querySelectorAll('#header').length).toEqual(7);
+ expect(document.querySelectorAll('#header .' + cn.columnCell).length).toEqual(7);
+ expect(document.querySelector('#header .' + cn.columnCell + ' text').textContent).toEqual('A');
+ done();
+ });
+ });
+ });
+
describe('basic use and basic data restyling', function() {
var mockCopy,
gd;