Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ui/public/vislib/__tests__/lib/x_axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ describe('Vislib xAxis Class Test Suite', function () {

dataObj = new Data(data, {}, persistedState);
xAxis = new Axis({
type: 'category',
el: $('.x-axis-div')[0],
xValues: dataObj.xValues(),
ordered: dataObj.get('ordered'),
Expand Down Expand Up @@ -217,7 +218,7 @@ describe('Vislib xAxis Class Test Suite', function () {
});

it('should create an xAxisFormatter function on the xAxis class', function () {
expect(_.isFunction(xAxis.xAxisFormatter)).to.be(true);
expect(_.isFunction(xAxis.axisFormatter)).to.be(true);
});
});

Expand Down
205 changes: 194 additions & 11 deletions src/ui/public/vislib/lib/axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import $ from 'jquery';
import _ from 'lodash';
import moment from 'moment';
import VislibLibErrorHandlerProvider from 'ui/vislib/lib/_error_handler';
import errors from 'ui/errors';

export default function AxisFactory(Private) {

const ErrorHandler = Private(VislibLibErrorHandlerProvider);
Expand All @@ -21,9 +23,13 @@ export default function AxisFactory(Private) {
this.el = args.el;
this.xValues = args.xValues;
this.ordered = args.ordered;
this.xAxisFormatter = args.xAxisFormatter;
this.axisFormatter = args.type === 'category' ? args.xAxisFormatter : args.yAxisFormatter;
this.expandLastBucket = args.expandLastBucket == null ? true : args.expandLastBucket;
this._attr = _.defaults(args._attr || {});
this.scale = null;
this.domain = [args.yMin, args.yMax];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is domain only used by the yAxis? It would make sense for xAxis too right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its only used for yAxis ... the code in this PR is pretty bad as i only joined the 2 files to make it clear how i progressed. yAxis and xAxis have many functions that actually do same/similar thing but are named differently and sometimes work in different ways (like on one axis you are supposed to pass a scale to function which returns domain and on the other one you are supposed to pass in limits and then apply the return value on scale ... i wouldn't worry about all that in this PR ... its just to show steps of progress ... so putting all the functions from both files into one ... then we can clearly see in the diffs what have been changed from here on ...

this.elSelector = args.type === 'category' ? '.x-axis-div' : '.y-axis-div';
this.type = args.type;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can move this up, and use this.type rather than args.type throughout

}

/**
Expand All @@ -33,7 +39,7 @@ export default function AxisFactory(Private) {
* @returns {D3.UpdateSelection} Appends x axis to visualization
*/
render() {
d3.select(this.el).selectAll('.x-axis-div').call(this.draw());
d3.select(this.el).selectAll(this.elSelector).call(this.draw());
};

/**
Expand Down Expand Up @@ -221,7 +227,7 @@ export default function AxisFactory(Private) {
this.xAxis = d3.svg.axis()
.scale(this.xScale)
.ticks(10)
.tickFormat(this.xAxisFormatter)
.tickFormat(this.axisFormatter)
.orient('bottom');
};

Expand All @@ -234,6 +240,9 @@ export default function AxisFactory(Private) {
draw() {
const self = this;
this._attr.isRotated = false;
const margin = this._attr.margin;
const mode = this._attr.mode;
const isWiggleOrSilhouette = (mode === 'wiggle' || mode === 'silhouette');

return function (selection) {
const n = selection[0].length;
Expand All @@ -247,21 +256,48 @@ export default function AxisFactory(Private) {
const width = parentWidth / n;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think .x-axis-div-wrapper above is going to be an issue.

const height = $(this.parentElement).height();

self.validateWidthandHeight(width, height);
/*
const width = $(el).parent().width();
const height = $(el).height();
*/
const adjustedHeight = height - margin.top - margin.bottom;

self.getXAxis(width);

self.validateWidthandHeight(width, height);

const svg = div.append('svg')
.attr('width', width)
.attr('height', height);

svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,0)')
.call(self.xAxis);
if (self.type === 'category') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably just move these two branches of code into helper functions, this._renderCategoryAxis() and this._renderValueAxis()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as in the following PRs i would make both axis to be more alike this IF statement will go away.

self.getXAxis(width);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,0)')
.call(self.xAxis);
} else {
const yAxis = self.getYAxis(adjustedHeight);
if (!isWiggleOrSilhouette) {
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + (width - 2) + ',' + margin.top + ')')
.call(yAxis);

const container = svg.select('g.y.axis').node();
if (container) {
const cWidth = Math.max(width, container.getBBox().width);
svg.attr('width', cWidth);
svg.select('g')
.attr('transform', 'translate(' + (cWidth - 2) + ',' + margin.top + ')');
}
}

}
});

selection.call(self.filterOrRotate());
if (self.type === 'category') {
selection.call(self.filterOrRotate());
}
};
};

Expand Down Expand Up @@ -346,6 +382,153 @@ export default function AxisFactory(Private) {
};
};

_isPercentage() {
return (this._attr.mode === 'percentage');
};

_isUserDefined() {
return (this._attr.setYExtents);
};

_isYExtents() {
return (this._attr.defaultYExtents);
};

_validateUserExtents(domain) {
const self = this;

return domain.map(function (val) {
val = parseInt(val, 10);

if (isNaN(val)) throw new Error(val + ' is not a valid number');
if (self._isPercentage() && self._attr.setYExtents) return val / 100;
return val;
});
};

_getExtents(domain) {
const min = domain[0];
const max = domain[1];

if (this._isUserDefined()) return this._validateUserExtents(domain);
if (this._isYExtents()) return domain;
if (this._attr.scale === 'log') return this._logDomain(min, max); // Negative values cannot be displayed with a log scale.
if (!this._isYExtents() && !this._isUserDefined()) return [Math.min(0, min), Math.max(0, max)];
return domain;
};

_throwCustomError(message) {
throw new Error(message);
};

_throwLogScaleValuesError() {
throw new errors.InvalidLogScaleValues();
};

/**
* Returns the appropriate D3 scale
*
* @param fnName {String} D3 scale
* @returns {*}
*/
_getScaleType(fnName) {
if (fnName === 'square root') fnName = 'sqrt'; // Rename 'square root' to 'sqrt'
fnName = fnName || 'linear';

if (typeof d3.scale[fnName] !== 'function') return this._throwCustomError('YAxis.getScaleType: ' + fnName + ' is not a function');

return d3.scale[fnName]();
};

/**
* Return the domain for log scale, i.e. the extent of the log scale.
* Log scales must begin at 1 since the log(0) = -Infinity
*
* @param {Number} min
* @param {Number} max
* @returns {Array}
*/
_logDomain(min, max) {
if (min < 0 || max < 0) return this._throwLogScaleValuesError();
return [1, max];
};

/**
* Creates the d3 y scale function
*
* @method getYScale
* @param height {Number} DOM Element height
* @returns {D3.Scale.QuantitiveScale|*} D3 yScale function
*/
getYScale(height) {
const scale = this._getScaleType(this._attr.scale);
const domain = this._getExtents(this.domain);

this.yScale = scale
.domain(domain)
.range([height, 0]);

if (!this._isUserDefined()) this.yScale.nice(); // round extents when not user defined
// Prevents bars from going off the chart when the y extents are within the domain range
if (this._attr.type === 'histogram') this.yScale.clamp(true);
return this.yScale;
};

getScaleType() {
return this._attr.scale;
};

tickFormat() {
const isPercentage = this._attr.mode === 'percentage';
if (isPercentage) return d3.format('%');
if (this.axisFormatter) return this.axisFormatter;
return d3.format('n');
};

_validateYScale(yScale) {
if (!yScale || _.isNaN(yScale)) throw new Error('yScale is ' + yScale);
};

/**
* Creates the d3 y axis function
*
* @method getYAxis
* @param height {Number} DOM Element height
* @returns {D3.Svg.Axis|*} D3 yAxis function
*/
getYAxis(height) {
const yScale = this.getYScale(height);
this._validateYScale(yScale);

// Create the d3 yAxis function
this.yAxis = d3.svg.axis()
.scale(yScale)
.tickFormat(this.tickFormat(this.domain))
.ticks(this.tickScale(height))
.orient('left');

return this.yAxis;
};

/**
* Create a tick scale for the y axis that modifies the number of ticks
* based on the height of the wrapping DOM element
* Avoid using even numbers in the yTickScale.range
* Causes the top most tickValue in the chart to be missing
*
* @method tickScale
* @param height {Number} DOM element height
* @returns {number} Number of y axis ticks
*/
tickScale(height) {
const yTickScale = d3.scale.linear()
.clamp(true)
.domain([20, 40, 1000])
.range([0, 3, 11]);

return Math.ceil(yTickScale(height));
};

/**
* Returns a string that is truncated to fit size
*
Expand Down Expand Up @@ -404,7 +587,7 @@ export default function AxisFactory(Private) {

if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
startX = myX + halfWidth;
return self.xAxisFormatter(d);
return self.axisFormatter(d);
} else {
d3.select(this.parentNode).remove();
}
Expand Down
7 changes: 3 additions & 4 deletions src/ui/public/vislib/lib/handler/types/point_series.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import VislibComponentsZeroInjectionInjectZerosProvider from 'ui/vislib/componen
import VislibLibHandlerHandlerProvider from 'ui/vislib/lib/handler/handler';
import VislibLibDataProvider from 'ui/vislib/lib/data';
import VislibLibAxisProvider from 'ui/vislib/lib/axis';
import VislibLibYAxisProvider from 'ui/vislib/lib/y_axis';
import VislibLibAxisTitleProvider from 'ui/vislib/lib/axis_title';
import VislibLibChartTitleProvider from 'ui/vislib/lib/chart_title';
import VislibLibAlertsProvider from 'ui/vislib/lib/alerts';
Expand All @@ -12,7 +11,6 @@ export default function ColumnHandler(Private) {
const Handler = Private(VislibLibHandlerHandlerProvider);
const Data = Private(VislibLibDataProvider);
const Axis = Private(VislibLibAxisProvider);
const YAxis = Private(VislibLibYAxisProvider);
const AxisTitle = Private(VislibLibAxisTitleProvider);
const ChartTitle = Private(VislibLibChartTitleProvider);
const Alerts = Private(VislibLibAlertsProvider);
Expand Down Expand Up @@ -40,6 +38,7 @@ export default function ColumnHandler(Private) {
axisTitle: new AxisTitle(vis.el, data.get('xAxisLabel'), data.get('yAxisLabel')),
chartTitle: new ChartTitle(vis.el),
xAxis: new Axis({
type : 'category',
el : vis.el,
xValues : data.xValues(),
ordered : data.get('ordered'),
Expand All @@ -48,15 +47,15 @@ export default function ColumnHandler(Private) {
_attr : vis._attr
}),
alerts: new Alerts(vis, data, opts.alerts),
yAxis: new YAxis({
yAxis: new Axis({
type : 'value',
el : vis.el,
yMin : isUserDefinedYAxis ? vis._attr.yAxis.min : data.getYMin(),
yMax : isUserDefinedYAxis ? vis._attr.yAxis.max : data.getYMax(),
yAxisFormatter: data.get('yAxisFormatter'),
_attr: vis._attr
})
});

};
}

Expand Down