Skip to content

Commit

Permalink
Merge pull request #493 from Kitware/test-treeheatmap
Browse files Browse the repository at this point in the history
Add basic test for TreeHeatmap
  • Loading branch information
jeffbaumes committed May 17, 2017
2 parents 8b965f0 + 542e6c5 commit 7b93bc4
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 25 deletions.
72 changes: 47 additions & 25 deletions components/TreeHeatmap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default class TreeHeatmap extends VisComponent {

constructor (el, options) {
super(el);
options = options || {};
this.data = options.data;
this.scale = options.scale || 'global';
this.clusterRows = options.clusterRows === undefined ? true : options.clusterRows;
Expand All @@ -99,9 +100,13 @@ export default class TreeHeatmap extends VisComponent {
render () {
d3.select(this.el).selectAll('*').remove();

if (this.data === undefined || this.data.length === 0) {
return;
}

let size = getElementSize(this.el);
let width = this.width || size.width;
let height = this.height || size.height;
let width = this.width || size.width || 400;
let height = this.height || size.height || 400;
let treeHeight = 100;
let labelHeight = 100;
let clusterRows = this.clusterRows;
Expand All @@ -119,16 +124,19 @@ export default class TreeHeatmap extends VisComponent {

let keys = Object.keys(this.data[0]);
let idColumn = this.idColumn;
if (!idColumn) {
if (idColumn === undefined) {
idColumn = keys.includes('_id') ? '_id' : idColumn;
idColumn = keys.includes('_') ? '_' : idColumn;
idColumn = keys.includes('') ? '' : idColumn;
}
if (idColumn === undefined) {
throw new Error('TreeHeatmap: No suitable idColumn found.');
}

let rows = [];
let reachedMetadata = false;
this.data.forEach(row => {
let id = row[idColumn];
let id = '' + row[idColumn];
if (id === '_child1') {
reachedMetadata = true;
}
Expand All @@ -151,7 +159,7 @@ export default class TreeHeatmap extends VisComponent {
let originalDataRows = {};
reachedMetadata = false;
this.data.forEach(row => {
let id = row[idColumn];
let id = '' + row[idColumn];
if (id === '_child1') {
reachedMetadata = true;
}
Expand All @@ -177,7 +185,7 @@ export default class TreeHeatmap extends VisComponent {
let colLinks = [];
if (clusterColumns) {
columns.forEach(col => {
if (+metadataRows._cluster[col.name] >= 0) {
if (metadataRows._cluster && +metadataRows._cluster[col.name] >= 0) {
colLinks.push({
cluster: +metadataRows._cluster[col.name],
child1: +metadataRows._child1[col.name],
Expand Down Expand Up @@ -272,7 +280,7 @@ export default class TreeHeatmap extends VisComponent {
for (let link = 0; link < links.length; link += 1) {
linkMap[links[link].cluster] = links[link];
}
let finalCluster = links[links.length - 1];
let finalCluster = links[links.length - 1] || {};
finalCluster.offset = 0;
finalCluster.parent = finalCluster;
for (let link = links.length - 1; link >= 0; link -= 1) {
Expand Down Expand Up @@ -336,32 +344,46 @@ export default class TreeHeatmap extends VisComponent {
];
});

group.selectAll('.tree-links')
.data(links)
.transition().duration(duration)
.attr('d', d => line(d.lines));
let treeLinks = group.selectAll('.tree-links')
.data(links);
if (duration > 0) {
treeLinks = treeLinks.transition().duration(duration);
}
treeLinks.attr('d', d => line(d.lines));

group.selectAll('.tree-select')
.data(reverseLinks).transition().duration(duration)
.attr(axis1, d => distanceScale(distance(d)))
let treeSelect = group.selectAll('.tree-select')
.data(reverseLinks);
if (duration > 0) {
treeSelect = treeSelect.transition().duration(duration);
}
treeSelect.attr(axis1, d => distanceScale(distance(d)))
.attr(axis2, d => treeScale(d.offset))
.attr(axis1 === 'x' ? 'width' : 'height', d => distanceScale(0) - distanceScale(distance(d)))
.attr(axis2 === 'x' ? 'width' : 'height', d => treeScale(d.offset + d.size) - treeScale(d.offset));

vis.selectAll('.datum')
.data(rectData).transition().duration(duration)
.attr('x', d => xScale(d.colIndex))
let rect = vis.selectAll('.datum')
.data(rectData);
if (duration > 0) {
rect = rect.transition().duration(duration);
}
rect.attr('x', d => xScale(d.colIndex))
.attr('y', d => yScale(d.rowIndex))
.attr('width', d => xScale(d.colIndex + 1) - xScale(d.colIndex))
.attr('height', d => yScale(d.rowIndex + 1) - yScale(d.rowIndex));

rowLabelGroup.selectAll('.row-label')
.data(rows).transition().duration(duration)
.attr('y', d => yScale(d.pos));
let rowLabel = rowLabelGroup.selectAll('.row-label')
.data(rows);
if (duration > 0) {
rowLabel = rowLabel.transition().duration(duration);
}
rowLabel.attr('y', d => yScale(d.pos));

colLabelGroup.selectAll('.col-label')
.data(columns).transition().duration(duration)
.attr('transform', d => 'translate(' + xScale(d.pos) + ',' + (yStart + rowSize) + ')');
let colLabel = colLabelGroup.selectAll('.col-label')
.data(columns);
if (duration > 0) {
colLabel = colLabel.transition().duration(duration);
}
colLabel.attr('transform', d => 'translate(' + xScale(d.pos) + ',' + (yStart + rowSize) + ')');
}

group.selectAll('.tree-select').data(reverseLinks).enter()
Expand All @@ -383,7 +405,7 @@ export default class TreeHeatmap extends VisComponent {

let rectData = [];

if (clusterRows) {
if (clusterRows && rows.length > 1) {
tree('vertical', rowLinks, rows, yStart, 0, rowSize, treeHeight, 1000);
} else {
rows.forEach((row) => {
Expand All @@ -392,7 +414,7 @@ export default class TreeHeatmap extends VisComponent {
row.pos = i + 0.5;
});
}
if (clusterColumns) {
if (clusterColumns && columns.length > 1) {
tree('horizontal', colLinks, columns, xStart, 0, colSize, treeHeight, 1000);
} else {
columns.forEach((col) => {
Expand Down
86 changes: 86 additions & 0 deletions components/TreeHeatmap/test/treeheatmap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import test from 'tape-catch';
import TreeHeatmap from '..';

test('TreeHeatmap component', t => {
t.equal(TreeHeatmap.options[0].type, 'table');

let el = document.createElement('div');
let vis = new TreeHeatmap(el);
vis.render();
t.equal(el.childNodes.length, 0, 'empty options should do nothing');

vis = new TreeHeatmap(el, {data: []});
vis.render();
t.equal(el.childNodes.length, 0, 'empty data should do nothing');

t.throws(() => {
vis = new TreeHeatmap(el, {data: [{}]});
vis.render();
}, /No suitable idColumn found/, 'no idColumn should throw error');

vis = new TreeHeatmap(el, {data: [{_id: 0, a: 0}]});
vis.render();
t.equal(el.childNodes.length, 1, 'single row and column should produce something');
console.log(el.innerHTML);
let single = document.createElement('div');

single.innerHTML = `<svg width="400px" height="400px">
<clipPath id="clip-rect"><rect x="100" y="100" width="200" height="200"></rect></clipPath>
<g clip-path="url(#clip-rect)"><rect class="datum" fill="rgb(255, 255, 255)" x="100" y="100" opacity="1" width="200" height="200"></rect></g>
<clipPath id="clip-row-labels"><rect x="300" y="100" width="100" height="200"></rect></clipPath>
<g clip-path="url(#clip-row-labels)"><text class="row-label" color="black" font-size="10px" alignment-baseline="middle" x="300" y="200">0</text></g>
<clipPath id="clip-col-labels"><rect x="100" y="300" width="200" height="100"></rect></clipPath>
<g clip-path="url(#clip-col-labels)"><g transform="translate(200,300)" class="col-label"><text color="black" font-size="10px" transform="rotate(-90)" text-anchor="end" alignment-baseline="middle">a</text></g></g>
</svg>`.replace(/\n/g, '').replace(/ {2}/g, '');

t.ok(el.isEqualNode(single), 'single row and column produce the right graphics');

vis = new TreeHeatmap(el, {
data: [
{_id: 0, a: 0, b: 1, _cluster: 2, _child1: 0, _child2: 1, _distance: 0.5, _size: 2},
{_id: 1, a: 1, b: 0},
{_id: '_cluster', a: 2},
{_id: '_child1', a: 0},
{_id: '_child2', a: 1},
{_id: '_distance', a: 0.5},
{_id: '_size', a: 2}
]});
vis.render();
t.equal(el.childNodes.length, 1, 'two rows and columns should produce something');
console.log(el.innerHTML);
let two = document.createElement('div');

two.innerHTML = `<svg width="400px" height="400px">
<clipPath id="clip-rect"><rect x="100" y="100" width="200" height="200"></rect></clipPath>
<g clip-path="url(#clip-rect)">
<rect class="datum" fill="rgb(255, 255, 255)" x="100" y="100" opacity="1" width="100" height="100"></rect>
<rect class="datum" fill="rgb(70, 130, 180)" x="200" y="100" opacity="1" width="100" height="100"></rect>
<rect class="datum" fill="rgb(70, 130, 180)" x="100" y="200" opacity="1" width="100" height="100"></rect>
<rect class="datum" fill="rgb(255, 255, 255)" x="200" y="200" opacity="1" width="100" height="100"></rect>
</g>
<clipPath id="clip-row-labels"><rect x="300" y="100" width="100" height="200"></rect></clipPath>
<g clip-path="url(#clip-row-labels)">
<text class="row-label" color="black" font-size="10px" alignment-baseline="middle" x="300" y="150">0</text>
<text class="row-label" color="black" font-size="10px" alignment-baseline="middle" x="300" y="250">1</text>
</g>
<clipPath id="clip-col-labels"><rect x="100" y="300" width="200" height="100"></rect></clipPath>
<g clip-path="url(#clip-col-labels)">
<g transform="translate(150,300)" class="col-label"><text color="black" font-size="10px" transform="rotate(-90)" text-anchor="end" alignment-baseline="middle">a</text></g>
<g transform="translate(250,300)" class="col-label"><text color="black" font-size="10px" transform="rotate(-90)" text-anchor="end" alignment-baseline="middle">b</text></g>
</g>
<clipPath id="clip-vertical"><rect x="0" y="100" width="100" height="200"></rect></clipPath>
<g class="vertical" clip-path="url(#clip-vertical)">
<path class="tree-links" d="M100,150L0,150L0,250L100,250" style="fill-opacity: 0; stroke: #000000;"></path>
<rect class="tree-select" x="0" y="100" width="100" height="200" style="fill: #4682b4; fill-opacity: 0;"></rect>
</g>
<clipPath id="clip-horizontal"><rect x="100" y="0" width="200" height="100"></rect></clipPath>
<g class="horizontal" clip-path="url(#clip-horizontal)">
<path class="tree-links" d="M150,100L150,0L250,0L250,100" style="fill-opacity: 0; stroke: #000000;"></path>
<rect class="tree-select" y="0" x="100" height="100" width="200" style="fill: #4682b4; fill-opacity: 0;"></rect>
</g>
</svg>`.replace(/\n/g, '').replace(/ {2}/g, '');

t.ok(el.isEqualNode(two), 'two rows and columns produce the right graphics');

t.end();
});

0 comments on commit 7b93bc4

Please sign in to comment.