Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Refactor targets (ready) #78

Merged
merged 5 commits into from

2 participants

@sym3tri
  • Added missing features to d3-ext.size(), height(), width()
  • Removed hardcoded target values from components, to remove assumptions and work with other layouts better.
  • Renamed gl-framed to gl-main
  • Instead of putting the container name in the css class attr, moved it to gl-container-name attr.
  • Made default stroke width 1.5, per discussion with UX.
  • Added d3.selection.prototype.selectAttr for convenience.
  • Fixed bug in d3-ext.util.applyTarget()
  • Cleaned up some of the silly layout manager tests.
@amrutac amrutac commented on the diff
examples/layouts.html
@@ -60,37 +60,39 @@
.data(dataConfig)
.render('#container');
- var lineGraph2 = glimpse.graphBuilder.create('line')
+ var lineGraph2 = glimpse.graph()
@amrutac Owner
amrutac added a note

Is this cause you want just one component per example?

@sym3tri
sym3tri added a note

It's b/c with these layouts it doesn't make sense to auto-add the avg/min/max like the graphBuilder does.

@amrutac Owner
amrutac added a note

Oh OK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@amrutac amrutac commented on the diff
src/d3-ext/select-attr.js
((4 lines not shown))
+ * certain value.
+ */
+define([
+ 'd3'
+],
+function(d3) {
+ 'use strict';
+
+ /**
+ * Selects a sub-selection of the current selection which is the first
+ * node that has a matching attr and value.
+ * @param attr The attribute to check.
+ * @param value The value that attr must have set.
+ * @return {d3.selection}
+ */
+ d3.selection.prototype.selectAttr = function(attr, value) {
@amrutac Owner
amrutac added a note

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@amrutac amrutac commented on the diff
src/layout/layoutmanager.js
@@ -193,15 +193,18 @@ function (layouts, array) {
node = getPaddingContainer(node, nodeInfo);
node.attr({
- 'class': nodeInfo['class'],
+ 'gl-container-name': nodeInfo.name,
@amrutac Owner
amrutac added a note

What's the reason for putting this in the gl-container-name attr instead of class?

@sym3tri
sym3tri added a note

It never really made sense to have this as a class. It implies that duplicates are ok, meaning that you could render to multiple containers (which we don't support).

I spoke to Johann about this before and he agreed that we should move it to a custom attribute too.

@amrutac Owner
amrutac added a note

Didn't think about the duplicates issue, good point! :+1:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/d3-ext/size.js
@@ -5,19 +5,57 @@
define(['d3'], function(d3) {
'use strict';
+ function isGnode(selection) {
@amrutac Owner
amrutac added a note

Missing docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/d3-ext/size.js
@@ -5,19 +5,57 @@
define(['d3'], function(d3) {
'use strict';
+ function isGnode(selection) {
+ return !selection.empty() && selection.node().tagName === 'g';
+ }
+
+ function lazyAddLayoutRect(selection) {
@amrutac Owner
amrutac added a note

missing docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/d3-ext/size.js
((22 lines not shown))
/**
* d3 selection width
* Returns width attribute of a non-group element.
* If element is a group,
* it returns the 'gl-width' attribute, if it's defined.
* else it returns the bounding box width.
+ * @param {Number} h
@amrutac Owner
amrutac added a note

@param {Number} w

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@amrutac amrutac commented on the diff
src/graphs/graph.js
((8 lines not shown))
if (!components_) {
return;
}
components_.forEach(function(component) {
- renderComponent_(component, selection);
+ component.render(root_);
@amrutac Owner
amrutac added a note

Shouldn't this be component.render(selection.select(component.config('target')) || root_)

@sym3tri
sym3tri added a note

So I was thinking about this a lot yesterday, and the target property actually only makes sense within the scope of a selection. So in this case our selection is root_ and the target points to another container within that root. If we allow rendering to targets without a root selection then target-based rendering won't work with multiple graphs on the page b/c it loses it's scope. We've only managed to avoid this problem by chance.

So I basically made the selection param passed to components.*.render(selection) required in all cases, and the config.target (aka sub-selector) is now optional. That being the case, I now always pass the root_ to component.render(). The extracted code in d3-ext.util.applyTarget handles narrowing to the sub-selection if a target is specified, otherwise it just renders to the passed selection param.

TLDR
selection and target used to both be optional. Now selection is required when calling render() to avoid scoping issues.

@amrutac Owner
amrutac added a note

Makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@amrutac amrutac commented on the diff
src/graphs/graph.js
@@ -132,6 +127,9 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
if (component.yScale) {
component.yScale(config_.yScale);
}
+ if (!component.config('target')) {
+ component.config('target', config_.primaryContainer);
+ }
@amrutac Owner
amrutac added a note

If the target of the component is not specified, shouldn't it just be rendered in the main svg element?

@sym3tri
sym3tri added a note

We could do it that way, but my goal here was trying to remove hardcoded coupling between our "Reach" layout and the way the components/graphs work. For example, before all the line components had config.target defaulted (and hardcoded) to .gl-framed, meaning that if we ever changed the layout, everything would break. So I removed it.

Now if we default to the svg element we must specify target in every graph.component() call that we make, which becomes cumbersome, so I added the primaryContainer config to be used as the default. Maybe I should check that the primary container config actually exists before setting it on components, and if not I can leave the component target as null which will force rendering to the root svg. Does that sounds like a good solution to you?

@amrutac Owner
amrutac added a note

Sounds good!

@sym3tri
sym3tri added a note

Actually I just realized that if primaryContainer is not set it will just be null, so in that case it the target property will be ignored, and the components will get rendered to the root. I think the code is ok as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@amrutac
Owner

Nice work with the refactoring! Other than the minor docs related changes LGTM.

@sym3tri sym3tri merged commit 96b16a8 into master
@sym3tri sym3tri deleted the refactor-targets branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 26, 2013
  1. fix(d3-ext.size) Fixed the size(), height(), width() functions to beh…

    Ed Rooth authored
    …ave as documented.
  2. refactor() Removed all assumptions of targets, and moved target names…

    Ed Rooth authored
    … to gl-container-name attr.
Commits on Mar 27, 2013
This page is out of date. Refresh to see the latest.
View
24 examples/layouts.html
@@ -60,37 +60,39 @@
.data(dataConfig)
.render('#container');
- var lineGraph2 = glimpse.graphBuilder.create('line')
+ var lineGraph2 = glimpse.graph()
@amrutac Owner
amrutac added a note

Is this cause you want just one component per example?

@sym3tri
sym3tri added a note

It's b/c with these layouts it doesn't make sense to auto-add the avg/min/max like the graphBuilder does.

@amrutac Owner
amrutac added a note

Oh OK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.config({
layout: {
- 'class': 'gl-vgroup',
- 'split': [70, 30],
+ name: 'gl-vgroup',
+ split: [70, 30],
children: [
{
- 'class': 'gl-framed',
+ name: 'gl-main',
border: 1,
borderColor: '#999',
backgroundColor: '#fff'
},
{
padding: 10,
- 'class': 'mygraph gl-info'
+ name: 'gl-info',
+ 'class': 'mygraph'
}
]
}
})
.legend({layout: 'horizontal', position: 'center'})
.data(dataConfig)
+ .component({ type: 'line', dataId: 'latencyDfw' })
.render('#container2');
- var lineGraph3 = glimpse.graphBuilder.create('line')
+ var lineGraph3 = glimpse.graph()
.config({
layout: {
- 'class': 'gl-hgroup',
- 'split': [70, 30],
+ name: 'gl-hgroup',
+ split: [70, 30],
children: [
{
- 'class': 'gl-framed',
+ name: 'gl-main',
border: 1,
borderColor: '#999',
backgroundColor: '#fff'
@@ -98,13 +100,15 @@
{
padding: 1,
paddingBottom: 10,
- 'class': 'mygraph gl-info'
+ 'class': 'mygraph',
+ name: 'gl-info'
}
]
}
})
.legend({layout: 'vertical', position: 'center'})
.data(dataConfig)
+ .component({ type: 'line', dataId: 'latencyDfw' })
.render('#container3');
View
2  src/components/area.js
@@ -24,7 +24,7 @@ function(array, config, obj, string, d3util, mixins, dataFns) {
defaults_ = {
type: 'area',
- target: '.gl-framed',
+ target: null,
cid: null,
xScale: null,
yScale: null,
View
2  src/components/axis.js
@@ -24,7 +24,7 @@ function(obj, config, string, mixins, d3util) {
defaults_ = {
type: 'x',
gap: 0,
- target: '.gl-framed',
+ target: null,
color: '#333',
opacity: 0.8,
fontFamily: 'arial',
View
11 src/components/label.js
@@ -26,11 +26,12 @@ function(obj, config, string, array, d3util, mixins) {
defaults_ = {
type: 'label',
- cid: undefined,
- dataId: undefined,
- cssClass: undefined,
- text: undefined,
- gap: undefined,
+ cid: null,
+ target: null,
+ dataId: null,
+ cssClass: null,
+ text: null,
+ gap: null,
layout: 'horizontal',
position: 'center-right',
color: '#333',
View
4 src/components/legend.js
@@ -29,8 +29,8 @@ function(obj, config, string, d3util, mixins) {
defaults_ = {
type: 'legend',
position: 'center-left',
- target: '.gl-info',
- cid: undefined,
+ target: null,
+ cid: null,
indicatorWidth: 10,
indicatorHeight: 10,
indicatorSpacing: 4,
View
6 src/components/line.js
@@ -24,15 +24,13 @@ function(array, config, obj, string, d3util, mixins, dataFns) {
defaults_ = {
type: 'line',
- target: '.gl-framed',
+ target: null,
cid: null,
- strokeWidth: 2,
color: null,
+ strokeWidth: 1.5,
inLegend: true,
lineGenerator: d3.svg.line(),
interpolate: 'linear',
- ease: 'linear',
- duration: 500,
opacity: 1
};
View
3  src/d3-ext/d3-ext.js
@@ -5,7 +5,8 @@ define([
'd3-ext/position',
'd3-ext/border',
'd3-ext/background-color',
- 'd3-ext/clip'
+ 'd3-ext/clip',
+ 'd3-ext/select-attr'
], function(d3) {
'use strict';
return d3;
View
24 src/d3-ext/select-attr.js
@@ -0,0 +1,24 @@
+/**
+ * @fileOverview
+ * Utility selector to select a single node which has an attr with a
+ * certain value.
+ */
+define([
+ 'd3'
+],
+function(d3) {
+ 'use strict';
+
+ /**
+ * Selects a sub-selection of the current selection which is the first
+ * node that has a matching attr and value.
+ * @param attr The attribute to check.
+ * @param value The value that attr must have set.
+ * @return {d3.selection}
+ */
+ d3.selection.prototype.selectAttr = function(attr, value) {
@amrutac Owner
amrutac added a note

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ return this.select('[' + attr + '="' + value + '"]');
+ };
+
+ return d3;
+});
View
121 src/d3-ext/size.js
@@ -6,18 +6,67 @@ define(['d3'], function(d3) {
'use strict';
/**
+ * Determines if the selection's node is a <g> node.
+ * @param {d3.selection} selection
+ * @return {Boolean}
+ */
+ function isGnode(selection) {
+ return !selection.empty() && selection.node().tagName === 'g';
+ }
+
+ /**
+ * Appends a layout rect if it doesn't exist, otherwise returns
+ * the existing one.
+ * @param {d3.selection} selection
+ * @return {d3.selection}
+ */
+ function lazyAddLayoutRect(selection) {
+ var layoutRect;
+
+ layoutRect = selection.select('.gl-layout');
+ if (layoutRect.empty()) {
+ layoutRect = selection.append('rect')
+ .attr({
+ class: 'gl-layout',
+ fill: 'none'
+ });
+ }
+ return layoutRect;
+ }
+
+ /**
* d3 selection width
* Returns width attribute of a non-group element.
* If element is a group,
* it returns the 'gl-width' attribute, if it's defined.
* else it returns the bounding box width.
+ * @param {Number} w
+ * @return {Number|d3.selection}
*/
- d3.selection.prototype.width = function() {
- var width = this.attr('gl-width');
- if (width) {
- return parseFloat(width);
+ d3.selection.prototype.width = function(w) {
+ var width, nonGnode;
+
+ // Getting.
+ if (!arguments.length) {
+ width = this.attr('gl-width');
+ if (width) {
+ return parseFloat(width);
+ }
+ return this.node().getBBox().width;
}
- return this.node().getBBox().width;
+ // Setting.
+ if (isGnode(this)) {
+ this.attr({
+ 'gl-width': w
+ });
+ nonGnode = lazyAddLayoutRect(this);
+ } else {
+ nonGnode = this;
+ }
+ nonGnode.attr({
+ width: w
+ });
+ return this;
};
/**
@@ -26,13 +75,33 @@ define(['d3'], function(d3) {
* If element is a group,
* it returns the 'gl-height' attribute, if it's defined.
* else it returns the bounding box height.
+ * @param {Number} h
+ * @return {Number|d3.selection}
*/
- d3.selection.prototype.height = function() {
- var height = this.attr('gl-height');
- if (height) {
- return parseFloat(height);
+ d3.selection.prototype.height = function(h) {
+ var height, nonGnode;
+
+ // Getting.
+ if (!arguments.length) {
+ height = this.attr('gl-height');
+ if (height) {
+ return parseFloat(height);
+ }
+ return this.node().getBBox().height;
}
- return this.node().getBBox().height;
+ // Setting.
+ if (isGnode(this)) {
+ this.attr({
+ 'gl-height': h
+ });
+ nonGnode = lazyAddLayoutRect(this);
+ } else {
+ nonGnode = this;
+ }
+ nonGnode.attr({
+ height: h
+ });
+ return this;
};
/**
@@ -40,29 +109,21 @@ define(['d3'], function(d3) {
* If element is a group,
* it sets the gl-width and gl-height attributes.
* else it returns width and height attribute of the element.
+ *
+ * @param {Number} width
+ * @param {Number} height
+ * @return {Array|d3.selection}
*/
d3.selection.prototype.size = function(width, height) {
- var rect;
-
- if (this.node().tagName === 'g') {
- rect = this.select('.gl-layout');
-
- if (rect.empty()) {
- rect = this.append('rect');
- }
-
- rect.attr({
- 'class': 'gl-layout',
- width: width,
- height: height,
- fill: 'none'
- });
-
- this.attr({ 'gl-width': width, 'gl-height': height });
- } else {
- this.attr({ width: width, height: height });
+ // No args, return current width/height.
+ if (!arguments.length) {
+ return [
+ this.width(),
+ this.height()
+ ];
}
- return this;
+ // Has args, set width/height.
+ return this.width(width).height(height);
};
return d3;
View
14 src/d3-ext/util.js
@@ -2,7 +2,9 @@
* @fileOverview
* Utility functions related to d3 selections.
*/
-define(
+define([
+ 'd3-ext/select-attr'
+],
function() {
'use strict';
@@ -30,9 +32,15 @@ function() {
* otherwise whatever fn returns.
*/
applyTarget: function(component, selection, fn) {
- var target;
+ var target, componentTarget;
+
+ target = this.select(selection);
+ componentTarget = component.config('target');
+ if (componentTarget) {
+ target = target.selectAttr('gl-container-name',
+ componentTarget);
+ }
- target = this.select(selection || component.config('target'));
if (!target || target.empty()) {
return null;
}
View
2  src/graphs/graph-builder.js
@@ -66,7 +66,7 @@ function(obj, array, string, graph) {
type: 'label',
dataId: 'gl-stats',
position: 'center-left',
- target: '.gl-footer'
+ target: 'gl-footer'
}];
/**
View
96 src/graphs/graph.js
@@ -40,9 +40,6 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
defaultXaccessor_,
defaultYaccessor_,
getComponent_,
- getFrameHeight_,
- getFrameWidth_,
- renderComponent_,
renderComponents_,
renderDefs_,
renderPanel_,
@@ -67,6 +64,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
coloredComponentsCount,
areComponentsRendered_,
hasTimeScale_;
+
/**
* @enum
* The possible states a graph can be in.
@@ -94,10 +92,6 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
viewBoxHeight: 230,
viewBoxWidth: 700,
preserveAspectRatio: 'none',
- marginTop: 10,
- marginRight: 0,
- marginBottom: 30,
- marginLeft: 0,
xScale: d3.time.scale.utc(),
yScale: d3.scale.linear(),
showLegend: true,
@@ -111,7 +105,8 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
yDomainModifier: 1.2,
colorPalette: d3.scale.category20().range(),
xAxisUnit: null,
- yAxisUnit: null
+ yAxisUnit: null,
+ primaryContainer: 'gl-main'
};
hasTimeScale_ = null;
@@ -132,6 +127,9 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
if (component.yScale) {
component.yScale(config_.yScale);
}
+ if (!component.config('target')) {
+ component.config('target', config_.primaryContainer);
+ }
@amrutac Owner
amrutac added a note

If the target of the component is not specified, shouldn't it just be rendered in the main svg element?

@sym3tri
sym3tri added a note

We could do it that way, but my goal here was trying to remove hardcoded coupling between our "Reach" layout and the way the components/graphs work. For example, before all the line components had config.target defaulted (and hardcoded) to .gl-framed, meaning that if we ever changed the layout, everything would break. So I removed it.

Now if we default to the svg element we must specify target in every graph.component() call that we make, which becomes cumbersome, so I added the primaryContainer config to be used as the default. Maybe I should check that the primary container config actually exists before setting it on components, and if not I can leave the component target as null which will force rendering to the root svg. Does that sounds like a good solution to you?

@amrutac Owner
amrutac added a note

Sounds good!

@sym3tri
sym3tri added a note

Actually I just realized that if primaryContainer is not set it will just be null, so in that case it the target property will be ignored, and the components will get rendered to the root. I think the code is ok as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
setDefaultColor(component);
components_.push(component);
};
@@ -194,22 +192,22 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
};
/**
- * Calculates the frame height
+ * Gets the main container selection.
* @private
- * @return {number}
+ * @return {d3.selection}
*/
- getFrameHeight_ = function() {
- return root_.select('.gl-framed').height();
- };
+ function getPrimaryContainer() {
+ return root_.selectAttr('gl-container-name', config_.primaryContainer);
+ }
/**
- * Calculates the frame width
+ * Calculates the main container width/height
* @private
- * @return {number}
+ * @return {Array} Array of numbers [width, height]
*/
- getFrameWidth_ = function() {
- return root_.select('.gl-framed').width();
- };
+ function getPrimaryContainerSize() {
+ return getPrimaryContainer().size();
+ }
/**
* Sets default color for on component if color not set
@@ -223,8 +221,8 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
colors = d3.functor(config_.colorPalette)();
len = colors.length;
if (component.hasOwnProperty('color')) {
- component.config().color = component.config().color ?
- component.config().color : colors[coloredComponentsCount++ % len];
+ component.config().color = component.config().color ||
+ colors[(coloredComponentsCount += 1) % len];
}
}
}
@@ -249,24 +247,17 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
/**
* Sets the target selection and calls render on each component
* @private
- * @param {d3.selection} selection
*/
- renderComponents_ = function(selection) {
+ renderComponents_ = function() {
if (!components_) {
return;
}
components_.forEach(function(component) {
- renderComponent_(component, selection);
+ component.render(root_);
@amrutac Owner
amrutac added a note

Shouldn't this be component.render(selection.select(component.config('target')) || root_)

@sym3tri
sym3tri added a note

So I was thinking about this a lot yesterday, and the target property actually only makes sense within the scope of a selection. So in this case our selection is root_ and the target points to another container within that root. If we allow rendering to targets without a root selection then target-based rendering won't work with multiple graphs on the page b/c it loses it's scope. We've only managed to avoid this problem by chance.

So I basically made the selection param passed to components.*.render(selection) required in all cases, and the config.target (aka sub-selector) is now optional. That being the case, I now always pass the root_ to component.render(). The extracted code in d3-ext.util.applyTarget handles narrowing to the sub-selection if a target is specified, otherwise it just renders to the passed selection param.

TLDR
selection and target used to both be optional. Now selection is required when calling render() to avoid scoping issues.

@amrutac Owner
amrutac added a note

Makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
});
areComponentsRendered_ = true;
};
- renderComponent_ = function(component, selection) {
- var renderTarget;
- renderTarget = selection.select(component.config('target')) || root_;
- component.render(renderTarget);
- };
-
/**
* Appends defs
* @private
@@ -384,7 +375,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
}
xExtents = [min, max];
- config_.xScale.rangeRound([0, getFrameWidth_()])
+ config_.xScale.rangeRound([0, getPrimaryContainerSize()[0]])
.domain(xExtents);
return xExtents;
};
@@ -401,7 +392,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
}
yExtents = d3.extent(yExtents);
- config_.yScale.rangeRound([getFrameHeight_(), 0])
+ config_.yScale.rangeRound([getPrimaryContainerSize()[1], 0])
.domain(yExtents);
return yExtents;
};
@@ -512,7 +503,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
};
/**
- * Displays the empty message over the framed area.
+ * Displays the empty message over the main container.
* @private
*/
showEmptyOverlay_ = function() {
@@ -548,11 +539,11 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
components: labels
});
addComponent_(overlay);
- overlay.render(root_.select('.gl-framed'));
+ overlay.render(root_);
};
/**
- * Displays the loading spinner and message over the framed area.
+ * Displays the loading spinner and message over the main container.
* @private
*/
showLoadingOverlay_ = function() {
@@ -575,11 +566,11 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
components: [spinner, label]
});
addComponent_(overlay);
- overlay.render(root_.select('.gl-framed'));
+ overlay.render(root_);
};
/**
- * Displays the error icon and message over the framed area.
+ * Displays the error icon and message over the main container.
* @private
*/
showErrorOverlay_ = function() {
@@ -602,7 +593,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
components: [icon, label]
});
addComponent_(overlay);
- overlay.render(root_.select('.gl-framed'));
+ overlay.render(root_);
};
/**
@@ -617,7 +608,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
addComponent_(xDomainLabel_);
}
update_();
- renderComponents_(root_);
+ renderComponents_();
}
/** Sets data on each component if data is set */
@@ -653,23 +644,26 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
dataCollection_ = collection.create();
xAxis_ = components.axis()
.config({
- 'type': 'x',
- 'orient': 'bottom',
- 'target': '.gl-xaxis',
- 'unit': config_.xAxisUnit
+ type: 'x',
+ orient: 'bottom',
+ target: 'gl-xaxis',
+ unit: config_.xAxisUnit
});
yAxis_ = components.axis()
.config({
- 'type': 'y',
- 'orient': 'right',
- 'tickPadding': 5,
- 'unit': config_.yAxisUnit
+ type: 'y',
+ orient: 'right',
+ tickPadding: 5,
+ unit: config_.yAxisUnit
+ });
+ legend_ = components.legend()
+ .config({
+ target: 'gl-info'
});
- legend_ = components.legend();
xDomainLabel_ = components.label()
.config({
cid: 'xDomainLabel',
- target: '.gl-footer',
+ target: 'gl-footer',
position: 'center-right'
});
coloredComponentsCount = 0;
@@ -760,7 +754,7 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
}
if (Array.isArray(data)) {
var i, len = data.length;
- for (i = 0; i < len; i++) {
+ for (i = 0; i < len; i += 1) {
upsertData_(data[i]);
}
} else {
@@ -794,11 +788,9 @@ function(obj, config, array, assetLoader, format, components, layoutManager,
component = components[componentConfig.type]();
component.config(componentConfig);
addComponent_(component);
-
if (isRendered_) {
- renderComponent_(component, root_);
+ component.render(root_);
}
-
return graph;
};
View
13 src/layout/layoutmanager.js
@@ -149,9 +149,9 @@ function (layouts, array) {
}
function applyLayout(node) {
- if (node.classed('gl-vgroup')) {
+ if (node.attr('gl-container-name') === 'gl-vgroup') {
node.layout({type: 'vertical'});
- } else if (node.classed('gl-hgroup')) {
+ } else if (node.attr('gl-container-name') === 'gl-hgroup') {
node.layout({type: 'horizontal'});
}
}
@@ -193,15 +193,18 @@ function (layouts, array) {
node = getPaddingContainer(node, nodeInfo);
node.attr({
- 'class': nodeInfo['class'],
+ 'gl-container-name': nodeInfo.name,
@amrutac Owner
amrutac added a note

What's the reason for putting this in the gl-container-name attr instead of class?

@sym3tri
sym3tri added a note

It never really made sense to have this as a class. It implies that duplicates are ok, meaning that you could render to multiple containers (which we don't support).

I spoke to Johann about this before and he agreed that we should move it to a custom attribute too.

@amrutac Owner
amrutac added a note

Didn't think about the duplicates issue, good point! :+1:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
'gl-split': nodeInfo.split
});
- if (node.classed('gl-vgroup')) {
+ if (nodeInfo.class) {
+ node.classed(nodeInfo.class, true);
+ }
+ if (node.attr('gl-container-name') === 'gl-vgroup') {
dim = calculateDim(nodeInfo.split, height);
dim = dim.map(function(d) {
return [width, d];
});
- } else if (node.classed('gl-hgroup')) {
+ } else if (node.attr('gl-container-name') === 'gl-hgroup') {
dim = calculateDim(nodeInfo.split, width);
dim = dim.map(function(d) {
return [d, height];
View
81 src/layout/layouts.js
@@ -9,48 +9,53 @@ define(function () {
var layouts = {
'default': {
- 'class': 'gl-vgroup',
- 'split': [10, 65, 10, 15],
- children: [{
- padding: 0,
- 'class': 'gl-info'
- }, {
- 'class': 'gl-framed',
- clip: true,
- border: 1,
- borderColor: '#999',
- backgroundColor: '#fff'
- }, {
- 'class': 'gl-xaxis',
- padding: 1,
- paddingTop: 20
- }, {
- 'class': 'gl-footer',
- paddingTop: 1,
- paddingBottom: 1,
- padding: 1,
- borderStyle: 'dotted',
- borderTop: 1
- }]
+ name: 'gl-vgroup',
+ split: [10, 65, 10, 15],
+ children: [
+ {
+ name: 'gl-info'
+ },
+ {
+ name: 'gl-main',
+ clip: true,
+ border: 1,
+ borderColor: '#999',
+ backgroundColor: '#fff'
+ },
+ {
+ name: 'gl-xaxis',
+ padding: 1,
+ paddingTop: 20
+ },
+ {
+ name: 'gl-footer',
+ paddingTop: 1,
+ paddingBottom: 1,
+ padding: 1,
+ borderStyle: 'dotted',
+ borderTop: 1
+ }
+ ]
},
'threepane': {
- 'class': 'gl-vgroup',
- 'split': [15, 70, 15],
- children: [{
- padding: 1,
- 'class': 'gl-stat'
- },{
- 'class': 'gl-unframed',
- padding: 1,
- paddingBottom: 10,
- children: {
- 'class': 'gl-framed'
+ name: 'gl-vgroup',
+ split: [15, 70, 15],
+ children: [
+ {
+ name: 'gl-stat',
+ padding: 1
+ },
+ {
+ name: 'gl-main',
+ padding: 1,
+ paddingBottom: 10
+ },
+ {
+ name: 'gl-info',
+ padding: 1
}
- },{
- padding: 1,
- 'class': 'gl-info'
- }]
+ ]
}
};
View
2  test/unit/components/line.spec.js
@@ -62,7 +62,7 @@ function(d3, object, line, dc) {
var config, defaults;
defaults = {
- strokeWidth: 2,
+ strokeWidth: 1.5,
color: 'steelBlue',
inLegend: true,
lineGenerator: d3.svg.line(),
View
38 test/unit/d3-ext/select-attr.spec.js
@@ -0,0 +1,38 @@
+define([
+ 'd3-ext/d3-ext'
+],
+function () {
+ 'use strict';
+
+ describe('d3.selection.prototype.selectAttr()', function() {
+
+ var selection, fixture, foo1a, foo1b, foo2;
+
+ beforeEach(function() {
+ fixture = jasmine.svgFixture();
+ foo1a = fixture.append('g')
+ .attr('gl-foo', '1').attr('id', 'foo1a');
+ foo1b = jasmine.svgFixture().append('g')
+ .attr('gl-foo', '1').attr('id', 'foo1b');
+ foo2 = jasmine.svgFixture().append('g')
+ .attr('gl-foo', '2').attr('id', 'foo2');
+ });
+
+ it('only selects 1 node', function() {
+ selection = fixture.selectAttr('gl-foo', '1');
+ expect(selection).toBeSelectionLength(1);
+ });
+
+ it('selects the first matching node', function() {
+ selection = fixture.selectAttr('gl-foo', '1');
+ expect(selection.node()).toBe(foo1a.node());
+ });
+
+ it('must match the value to get a result', function() {
+ selection = fixture.selectAttr('gl-foo', 'doesnt exist value');
+ expect(selection.empty()).toBe(true);
+ });
+
+ });
+
+});
View
148 test/unit/d3-ext/size.spec.js
@@ -4,7 +4,7 @@ define([
function() {
'use strict';
- var g, rect;
+ var g, rect, layoutRect;
describe('selection.size', function() {
@@ -15,28 +15,48 @@ function() {
describe('selection.size', function() {
- it('appends the rectange and set width, height attributes', function() {
+ beforeEach(function() {
g.size(400, 200);
- expect(g.node()).toHaveXML([
- '<g gl-width="400" gl-height="200">',
- '<rect class="gl-layout" ',
- 'width="400" height="200" fill="none"/>',
- '</g>'].join(''));
+ layoutRect = g.select('rect');
+ });
+
+ it('sets the g.gl-height', function() {
+ expect(g.node()).toHaveAttr('gl-height', 200);
+ });
+
+ it('sets the g.gl-width', function() {
+ expect(g.node()).toHaveAttr('gl-width', 400);
+ });
+
+ it('appends the rect', function() {
+ expect(layoutRect).toBeSelectionLength(1);
+ });
+
+ it('sets the gl-layout class on the rect', function() {
+ expect(layoutRect.node()).toHaveClasses('gl-layout');
+ });
+
+ it('sets the width on the rect', function() {
+ expect(layoutRect.node()).toHaveAttr('width', 400);
+ });
+
+ it('sets the height on the rect', function() {
+ expect(layoutRect.node()).toHaveAttr('height', 200);
+ });
+
+ it('sets the rect fill to none', function() {
+ expect(layoutRect.node()).toHaveAttr('fill', 'none');
});
it('does not append a second rectangle', function() {
- rect.size(400, 200);
- expect(rect.node()).toHaveAttr({
- width: 400,
- height: 200
- });
+ g.size(400, 200);
+ expect(g.selectAll('rect')).toBeSelectionLength(1);
});
it('only appends rect only once and updates attrs on subsequent calls',
function() {
var testRect;
- g.size(400, 200);
g.size(300, 200);
testRect = g.selectAll('rect');
expect(testRect[0].length).toBe(1);
@@ -48,58 +68,86 @@ function() {
});
- describe('selection.width', function() {
+ describe('seleciton.width() / selection.height()', function() {
beforeEach(function() {
- g.size(12, 15);
- rect.size(20,40);
+ g.height(15);
+ g.width(12);
+ rect.height(40);
+ rect.width(20);
+ layoutRect = g.select('rect.gl-layout');
});
- it('returns correct width of group', function() {
- expect(g.width()).toBe(12);
- });
+ describe('selection.width', function() {
- it('returns correct width of rect', function() {
- expect(rect.width()).toBe(20);
- });
+ it('returns correct width of group', function() {
+ expect(g.width()).toBe(12);
+ });
- it('returns width attribute of group', function() {
- g.append('rect').size(1000, 1000);
- expect(g.width()).toBe(12);
- });
+ it('returns correct width of rect', function() {
+ expect(rect.width()).toBe(20);
+ });
- it('returns BBox width if width attr of group is not set', function() {
- g.append('rect').size(1000, 1000);
- g.attr('gl-width', null);
- expect(g.width()).toBe(1000);
- });
+ it('returns width attribute of group', function() {
+ g.append('rect').width(1000).height(1);
+ expect(g.width()).toBe(12);
+ });
- });
+ it('returns BBox width if width attr of group is not set', function() {
+ g.append('rect').width(1000).height(1);
+ g.attr('gl-width', null);
+ expect(g.width()).toBe(1000);
+ });
- describe('selection.height', function() {
+ it('sets the gl-width of a <g>', function() {
+ expect(g.node()).toHaveAttr('gl-width', '12');
+ });
- beforeEach(function() {
- g.size(12, 15);
- rect.size(20,40);
- });
+ it('adds a layout rect for <g> elements', function() {
+ expect(layoutRect.empty()).toBe(false);
+ });
- it('returns correct hieght of group', function() {
- expect(g.height()).toBe(15);
- });
+ it('sets the width of the layout rect', function() {
+ expect(layoutRect.node()).toHaveAttr('width', '12');
+ expect(layoutRect.width()).toBe(12);
+ });
- it('returns correct height of rect', function() {
- expect(rect.height()).toBe(40);
});
- it('returns height attribute of group', function() {
- g.append('rect').size(1000, 1000);
- expect(g.height()).toBe(15);
- });
+ describe('selection.height', function() {
+
+ it('returns correct hieght of group', function() {
+ expect(g.height()).toBe(15);
+ });
+
+ it('returns correct height of rect', function() {
+ expect(rect.height()).toBe(40);
+ });
+
+ it('returns height attribute of group', function() {
+ g.append('rect').height(1000).width(1);
+ expect(g.height()).toBe(15);
+ });
+
+ it('returns BBox height if width attr of group is not set', function() {
+ g.append('rect').height(1000).width(1);
+ g.attr('gl-height', null);
+ expect(g.height()).toBe(1000);
+ });
+
+ it('sets the gl-height of a <g>', function() {
+ expect(g.node()).toHaveAttr('gl-height', '15');
+ });
+
+ it('adds a layout rect for <g> elements', function() {
+ expect(layoutRect.empty()).toBe(false);
+ });
+
+ it('sets the height of the layout rect', function() {
+ expect(layoutRect.node()).toHaveAttr('height', '15');
+ expect(layoutRect.height()).toBe(15);
+ });
- it('returns BBox height if width attr of group is not set', function() {
- g.append('rect').size(1000, 1000);
- g.attr('gl-height', null);
- expect(g.height()).toBe(1000);
});
});
View
22 test/unit/d3-ext/util.spec.js
@@ -34,7 +34,7 @@ function(d3util) {
});
describe('.applyTarget()', function() {
- var componentMock, applyFn, noop;
+ var componentMock, applyFn, noop, mockTarget;
function getComponentMock(target) {
return {
@@ -50,9 +50,11 @@ function(d3util) {
};
noop = function() {};
fixture = jasmine.svgFixture();
+ mockTarget = fixture.append('g')
+ .attr('gl-container-name', 'foo-container');
});
- it('returns null if there is no selection or target', function() {
+ it('returns null if there is no selection', function() {
componentMock = getComponentMock(null);
expect(
d3util.applyTarget(componentMock, null, noop)
@@ -67,20 +69,12 @@ function(d3util) {
).toBe(fixture);
});
- it('passes config.target selection as the target if selection is null',
+ it('subselects the target within the selection if both are present',
function() {
- componentMock = getComponentMock('#svg-fixture');
+ componentMock = getComponentMock('foo-container');
expect(
- d3util.applyTarget(componentMock, null, applyFn).node()
- ).toBe(d3.select('#svg-fixture').node());
- });
-
- it('passes the selection as the target if both are present',
- function() {
- componentMock = getComponentMock('#svg-fixture');
- expect(
- d3util.applyTarget(componentMock, fixture, applyFn)
- ).toBe(fixture);
+ d3util.applyTarget(componentMock, fixture, applyFn).node()
+ ).toBe(mockTarget.node());
});
});
View
25 test/unit/graphs/graph.spec.js
@@ -23,10 +23,6 @@ function(graph, assetLoader, dc, compUtil, dataFns) {
dataCollection;
defaults = {
- marginTop: 10,
- marginRight: 0,
- marginBottom: 30,
- marginLeft: 0
};
epochBaseMs = 0;
@@ -143,22 +139,6 @@ function(graph, assetLoader, dc, compUtil, dataFns) {
config = testGraph.config();
});
- it('has default marginTop', function() {
- expect(config.marginTop).toBe(defaults.marginTop);
- });
-
- it('has default marginRight', function() {
- expect(config.marginRight).toBe(defaults.marginRight);
- });
-
- it('has default marginBottom', function() {
- expect(config.marginBottom).toBe(defaults.marginBottom);
- });
-
- it('has default marginLeft', function() {
- expect(config.marginLeft).toBe(defaults.marginLeft);
- });
-
it('has default xScale set', function() {
expect(config.xScale.toString()).toBe(d3.time.scale().toString());
});
@@ -427,8 +407,9 @@ function(graph, assetLoader, dc, compUtil, dataFns) {
expect(defs.node().nodeName.toLowerCase()).toBe('defs');
});
- it('renders framed components group', function() {
- var group = panel.select('.gl-framed');
+ it('renders the primary container', function() {
+ var group = panel.selectAttr('gl-container-name',
+ testGraph.config('primaryContainer'));
expect(group.node().nodeName.toLowerCase()).toBe('g');
});
View
113 test/unit/layout/layoutmanager.spec.js
@@ -18,8 +18,6 @@ function (lm) {
fixture = jasmine.svgFixture().node();
}
- describe('render layout template', function () {
-
// TODO: fix this rediculous test.
//it('renders default layout template', function () {
@@ -62,17 +60,50 @@ function (lm) {
//]));
//});
- it('renders layout 1', function () {
+ describe('render simple layout', function () {
+ var gNode, rectNode;
+
+ beforeEach(function() {
applyLayout({
'class': 'someclass'
});
- expect(fixture).toHaveXML(xmlString([
- '<g gl-width="200" gl-height="200" class="someclass">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
- '</g>'
- ]));
+ gNode = fixture.childNodes[0];
+ rectNode = gNode.childNodes[0];
+ });
+
+ it('renders a <g>', function() {
+ expect(gNode.tagName).toBe('g');
+ });
+
+ it('has the correct <g> node attrs', function() {
+ expect(gNode).toHaveAttr({
+ 'gl-width': 200,
+ 'gl-height': 200,
+ 'class': 'someclass'
+ });
+ });
+
+ it('only renders 1 child node under <g>', function() {
+ expect(gNode.childNodes.length).toBe(1);
+ });
+
+ it('renders the <rect> node', function() {
+ expect(rectNode.tagName).toBe('rect');
});
+ it('renders correct <rect> node attrs', function() {
+ expect(rectNode).toHaveAttr({
+ class: 'gl-layout',
+ fill: 'none',
+ width: 200,
+ height: 200
+ });
+ });
+
+ });
+
+ describe('renders complex layouts', function() {
+
it('renders layout 2', function () {
applyLayout({
'class': 'someclass',
@@ -80,9 +111,9 @@ function (lm) {
});
expect(fixture).toHaveXML(xmlString([
'<g gl-width="200" gl-height="200" class="someclass">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'<g gl-width="200" gl-height="200" class="someotherclass">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'</g>',
'</g>'
]));
@@ -90,7 +121,8 @@ function (lm) {
it('renders layout 3 - vgroup', function() {
applyLayout({
- 'class': 'gl-vgroup someclass',
+ name: 'gl-vgroup',
+ 'class': 'someclass',
'split': [50, 50],
children: [{
'class': 'box1'
@@ -99,21 +131,22 @@ function (lm) {
}]
});
expect(fixture).toHaveXML(xmlString([
- '<g gl-width="200" gl-height="200" class="gl-vgroup someclass" gl-split="50,50">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<g gl-width="200" gl-height="200" gl-container-name="gl-vgroup" gl-split="50,50" class="someclass">',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'<g gl-width="200" gl-height="100" class="box1" transform="translate(0,0)">',
- '<rect class="gl-layout" width="200" height="100" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="100"/>',
'</g>',
'<g gl-width="200" gl-height="100" class="box2" transform="translate(0,100)">',
- '<rect class="gl-layout" width="200" height="100" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="100"/>',
'</g>',
'</g>']));
});
it('renders layout 4 - hgroup', function() {
applyLayout({
- 'class': 'gl-hgroup someclass',
- 'split': [50, 50],
+ 'class': 'someclass',
+ name: 'gl-hgroup',
+ split: [50, 50],
children: [{
'class': 'box1'
},{
@@ -121,13 +154,13 @@ function (lm) {
}]
});
expect(fixture).toHaveXML(xmlString([
- '<g gl-width="200" gl-height="200" class="gl-hgroup someclass" gl-split="50,50">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<g gl-width="200" gl-height="200" gl-container-name="gl-hgroup" gl-split="50,50" class="someclass">',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'<g gl-width="100" gl-height="200" class="box1" transform="translate(0,0)">',
- '<rect class="gl-layout" width="100" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="100" height="200"/>',
'</g>',
'<g gl-width="100" gl-height="200" class="box2" transform="translate(100,0)">',
- '<rect class="gl-layout" width="100" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="100" height="200"/>',
'</g>',
'</g>']));
});
@@ -138,7 +171,8 @@ function (lm) {
it('hgroup - padding', function() {
applyLayout({
- 'class': 'gl-hgroup someclass',
+ name: 'gl-hgroup',
+ 'class': 'someclass',
'split': [50, 50],
children: [{
padding: 2,
@@ -149,18 +183,18 @@ function (lm) {
}]
});
expect(fixture).toHaveXML(xmlString([
- '<g gl-width="200" gl-height="200" class="gl-hgroup someclass" gl-split="50,50">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<g gl-width="200" gl-height="200" gl-container-name="gl-hgroup" gl-split="50,50" class="someclass">',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'<g gl-width="100" gl-height="200" transform="translate(0,0)">',
- '<rect class="gl-layout" width="100" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="100" height="200"/>',
'<g gl-padding="2" gl-width="96" gl-height="192" transform="translate(2,4)" class="box1">',
- '<rect class="gl-layout" width="96" height="192" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="96" height="192"/>',
'</g>',
'</g>',
'<g gl-width="100" gl-height="200" transform="translate(100,0)">',
- '<rect class="gl-layout" width="100" height="200" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="100" height="200"/>',
'<g gl-padding="4" gl-width="92" gl-height="184" transform="translate(4,8)" class="box2">',
- '<rect class="gl-layout" width="92" height="184" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="92" height="184"/>',
'</g>',
'</g>',
'</g>'
@@ -169,29 +203,30 @@ function (lm) {
it('vgroup - padding', function() {
applyLayout({
- 'class': 'gl-vgroup someclass',
+ 'class': 'someclass',
+ name: 'gl-vgroup',
'split': [50, 50],
children: [{
padding: 2,
- 'class': 'box1'
+ name: 'box1'
},{
padding: 4,
- 'class': 'box2'
+ name: 'box2'
}]
});
expect(fixture).toHaveXML(xmlString([
- '<g gl-width="200" gl-height="200" class="gl-vgroup someclass" gl-split="50,50">',
- '<rect class="gl-layout" width="200" height="200" fill="none"/>',
+ '<g gl-width="200" gl-height="200" gl-container-name="gl-vgroup" gl-split="50,50" class="someclass">',
+ '<rect class="gl-layout" fill="none" width="200" height="200"/>',
'<g gl-width="200" gl-height="100" transform="translate(0,0)">',
- '<rect class="gl-layout" width="200" height="100" fill="none"/>',
- '<g gl-padding="2" gl-width="192" gl-height="96" transform="translate(4,2)" class="box1">',
- '<rect class="gl-layout" width="192" height="96" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="100"/>',
+ '<g gl-padding="2" gl-width="192" gl-height="96" transform="translate(4,2)" gl-container-name="box1">',
+ '<rect class="gl-layout" fill="none" width="192" height="96"/>',
'</g>',
'</g>',
'<g gl-width="200" gl-height="100" transform="translate(0,100)">',
- '<rect class="gl-layout" width="200" height="100" fill="none"/>',
- '<g gl-padding="4" gl-width="184" gl-height="92" transform="translate(8,4)" class="box2">',
- '<rect class="gl-layout" width="184" height="92" fill="none"/>',
+ '<rect class="gl-layout" fill="none" width="200" height="100"/>',
+ '<g gl-padding="4" gl-width="184" gl-height="92" transform="translate(8,4)" gl-container-name="box2">',
+ '<rect class="gl-layout" fill="none" width="184" height="92"/>',
'</g>',
'</g>',
'</g>'
Something went wrong with that request. Please try again.