mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
[explore] improve bubble viz (#2927)
* [explore] improve bubble viz * allow for custom axis formatters * reorg the control panels * Addressing comments
This commit is contained in:
parent
591e512327
commit
3e51c61dbf
@ -7,7 +7,7 @@ import ColumnOption from '../../components/ColumnOption';
|
||||
const D3_FORMAT_DOCS = 'D3 format syntax: https://github.com/d3/d3-format';
|
||||
|
||||
// input choices & options
|
||||
const D3_TIME_FORMAT_OPTIONS = [
|
||||
const D3_FORMAT_OPTIONS = [
|
||||
['.3s', '.3s | 12.3k'],
|
||||
['.3%', '.3% | 1234543.210%'],
|
||||
['.4r', '.4r | 12350'],
|
||||
@ -20,8 +20,7 @@ const ROW_LIMIT_OPTIONS = [10, 50, 100, 250, 500, 1000, 5000, 10000, 50000];
|
||||
|
||||
const SERIES_LIMITS = [0, 5, 10, 25, 50, 100, 500];
|
||||
|
||||
|
||||
export const TIME_STAMP_OPTIONS = [
|
||||
export const D3_TIME_FORMAT_OPTIONS = [
|
||||
['smart_date', 'Adaptative formating'],
|
||||
['%m/%d/%Y', '%m/%d/%Y | 01/14/2019'],
|
||||
['%Y-%m-%d', '%Y-%m-%d | 2019-01-14'],
|
||||
@ -594,7 +593,7 @@ export const controls = {
|
||||
label: 'Number format',
|
||||
renderTrigger: true,
|
||||
default: '.3s',
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
choices: D3_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
},
|
||||
|
||||
@ -682,8 +681,8 @@ export const controls = {
|
||||
type: 'SelectControl',
|
||||
label: 'Y Axis',
|
||||
default: null,
|
||||
description: 'Metric assigned to the [Y] axis',
|
||||
validators: [v.nonEmpty],
|
||||
description: 'Metric assigned to the [Y] axis',
|
||||
optionRenderer: m => <MetricOption metric={m} />,
|
||||
valueRenderer: m => <MetricOption metric={m} />,
|
||||
valueKey: 'metric_name',
|
||||
@ -765,7 +764,7 @@ export const controls = {
|
||||
freeForm: true,
|
||||
label: 'Table Timestamp Format',
|
||||
default: 'smart_date',
|
||||
choices: TIME_STAMP_OPTIONS,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: 'Timestamp Format',
|
||||
},
|
||||
|
||||
@ -788,12 +787,22 @@ export const controls = {
|
||||
},
|
||||
|
||||
x_axis_format: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: 'X Axis Format',
|
||||
renderTrigger: true,
|
||||
default: '.3s',
|
||||
choices: D3_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
},
|
||||
|
||||
x_axis_time_format: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: 'X Axis Format',
|
||||
renderTrigger: true,
|
||||
default: 'smart_date',
|
||||
choices: TIME_STAMP_OPTIONS,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
},
|
||||
|
||||
@ -803,7 +812,7 @@ export const controls = {
|
||||
label: 'Y Axis Format',
|
||||
renderTrigger: true,
|
||||
default: '.3s',
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
choices: D3_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
},
|
||||
|
||||
@ -812,7 +821,7 @@ export const controls = {
|
||||
freeForm: true,
|
||||
label: 'Right Axis Format',
|
||||
default: '.3s',
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
choices: D3_FORMAT_OPTIONS,
|
||||
description: D3_FORMAT_DOCS,
|
||||
},
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { D3_TIME_FORMAT_OPTIONS } from './controls';
|
||||
|
||||
export const sections = {
|
||||
druidTimeSeries: {
|
||||
label: 'Time',
|
||||
@ -143,6 +145,11 @@ const visTypes = {
|
||||
},
|
||||
sections.NVD3TimeSeries[1],
|
||||
],
|
||||
controlOverrides: {
|
||||
x_axis_format: {
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
dual_line: {
|
||||
@ -158,15 +165,13 @@ const visTypes = {
|
||||
{
|
||||
label: 'Y Axis 1',
|
||||
controlSetRows: [
|
||||
['metric'],
|
||||
['y_axis_format'],
|
||||
['metric', 'y_axis_format'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Y Axis 2',
|
||||
controlSetRows: [
|
||||
['metric_2'],
|
||||
['y_axis_2_format'],
|
||||
['metric_2', 'y_axis_2_format'],
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -410,19 +415,41 @@ const visTypes = {
|
||||
label: null,
|
||||
controlSetRows: [
|
||||
['series', 'entity'],
|
||||
['x', 'y'],
|
||||
['size', 'limit'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Chart Options',
|
||||
controlSetRows: [
|
||||
['show_legend', 'max_bubble_size'],
|
||||
['x_axis_label', 'y_axis_label'],
|
||||
['x_log_scale', 'y_log_scale'],
|
||||
['show_legend', null],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Bubbles',
|
||||
controlSetRows: [
|
||||
['size', 'max_bubble_size'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'X Axis',
|
||||
controlSetRows: [
|
||||
['x', 'x_axis_format'],
|
||||
['x_axis_label', 'x_log_scale'],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Y Axis',
|
||||
controlSetRows: [
|
||||
['y', 'y_axis_format'],
|
||||
['y_axis_label', 'y_log_scale'],
|
||||
],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
x_axis_format: {
|
||||
default: '.3s',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
bullet: {
|
||||
|
@ -2,7 +2,7 @@ import moment from 'moment';
|
||||
|
||||
const d3 = require('d3');
|
||||
|
||||
function UTC(dttm) {
|
||||
export function UTC(dttm) {
|
||||
return new Date(
|
||||
dttm.getUTCFullYear(),
|
||||
dttm.getUTCMonth(),
|
||||
@ -67,13 +67,6 @@ export const formatDate = function (dttm) {
|
||||
// d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
|
||||
return tickMultiFormat(d);
|
||||
};
|
||||
export const timeFormatFactory = function (d3timeFormat) {
|
||||
const f = d3.time.format(d3timeFormat);
|
||||
return function (dttm) {
|
||||
const d = UTC(new Date(dttm));
|
||||
return f(d);
|
||||
};
|
||||
};
|
||||
export const fDuration = function (t1, t2, f = 'HH:mm:ss.SS') {
|
||||
const diffSec = t2 - t1;
|
||||
const duration = moment(new Date(diffSec));
|
||||
|
@ -1,6 +1,29 @@
|
||||
/* eslint camelcase: 0 */
|
||||
const d3 = require('d3');
|
||||
const $ = require('jquery');
|
||||
import d3 from 'd3';
|
||||
import $ from 'jquery';
|
||||
|
||||
import { formatDate, UTC } from './dates';
|
||||
|
||||
export function d3FormatPreset(format) {
|
||||
// like d3.format, but with support for presets like 'smart_date'
|
||||
if (format === 'smart_date') {
|
||||
return formatDate;
|
||||
}
|
||||
if (format) {
|
||||
return d3.format(format);
|
||||
}
|
||||
return d3.format('.3s');
|
||||
}
|
||||
export const d3TimeFormatPreset = function (format) {
|
||||
if (format === 'smart_date') {
|
||||
return formatDate;
|
||||
}
|
||||
const f = d3.time.format(format);
|
||||
return function (dttm) {
|
||||
const d = UTC(new Date(dttm));
|
||||
return f(d);
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Utility function that takes a d3 svg:text selection and a max width, and splits the
|
||||
|
@ -3,7 +3,6 @@ import { expect } from 'chai';
|
||||
import {
|
||||
tickMultiFormat,
|
||||
formatDate,
|
||||
timeFormatFactory,
|
||||
fDuration,
|
||||
now,
|
||||
epochTimeXHoursAgo,
|
||||
@ -23,12 +22,6 @@ describe('formatDate', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('timeFormatFactory', () => {
|
||||
it('is a function', () => {
|
||||
assert.isFunction(timeFormatFactory);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fDuration', () => {
|
||||
it('is a function', () => {
|
||||
assert.isFunction(fDuration);
|
||||
|
@ -2,6 +2,7 @@ import { it, describe } from 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
tryNumify, slugify, formatSelectOptionsForRange, d3format,
|
||||
d3FormatPreset, d3TimeFormatPreset,
|
||||
} from '../../../javascripts/modules/utils';
|
||||
|
||||
describe('utils', () => {
|
||||
@ -35,4 +36,20 @@ describe('utils', () => {
|
||||
expect(d3format('.3s', 1237)).to.equal('1.24k');
|
||||
expect(d3format('', 1237)).to.equal('1.24k');
|
||||
});
|
||||
describe('d3FormatPreset', () => {
|
||||
it('is a function', () => {
|
||||
assert.isFunction(d3FormatPreset);
|
||||
});
|
||||
it('returns a working formatter', () => {
|
||||
expect(d3FormatPreset('.3s')(3000000)).to.equal('3.00M');
|
||||
});
|
||||
});
|
||||
describe('d3TimeFormatPreset', () => {
|
||||
it('is a function', () => {
|
||||
assert.isFunction(d3TimeFormatPreset);
|
||||
});
|
||||
it('returns a working time formatter', () => {
|
||||
expect(d3FormatPreset('smart_date')(0)).to.equal('1970');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,20 +2,19 @@
|
||||
import $ from 'jquery';
|
||||
import throttle from 'lodash.throttle';
|
||||
import d3 from 'd3';
|
||||
import nv from 'nvd3';
|
||||
|
||||
import { category21 } from '../javascripts/modules/colors';
|
||||
import { timeFormatFactory, formatDate } from '../javascripts/modules/dates';
|
||||
import { customizeToolTip, tryNumify } from '../javascripts/modules/utils';
|
||||
import { customizeToolTip, d3TimeFormatPreset, d3FormatPreset, tryNumify } from '../javascripts/modules/utils';
|
||||
|
||||
import { TIME_STAMP_OPTIONS } from '../javascripts/explore/stores/controls';
|
||||
import { D3_TIME_FORMAT_OPTIONS } from '../javascripts/explore/stores/controls';
|
||||
|
||||
const nv = require('nvd3');
|
||||
|
||||
// CSS
|
||||
require('../node_modules/nvd3/build/nv.d3.min.css');
|
||||
require('./nvd3_vis.css');
|
||||
import '../node_modules/nvd3/build/nv.d3.min.css';
|
||||
import './nvd3_vis.css';
|
||||
|
||||
const timeStampFormats = TIME_STAMP_OPTIONS.map(opt => opt[0]);
|
||||
const timeStampFormats = D3_TIME_FORMAT_OPTIONS.map(opt => opt[0]);
|
||||
const minBarWidth = 15;
|
||||
const animationTime = 1000;
|
||||
|
||||
@ -317,16 +316,6 @@ function nvd3Vis(slice, payload) {
|
||||
if (fd.x_log_scale) {
|
||||
chart.xScale(d3.scale.log());
|
||||
}
|
||||
let xAxisFormatter;
|
||||
if (vizType === 'bubble') {
|
||||
xAxisFormatter = d3.format('.3s');
|
||||
} else if (fd.x_axis_format === 'smart_date') {
|
||||
xAxisFormatter = formatDate;
|
||||
chart.xAxis.tickFormat(xAxisFormatter);
|
||||
} else if (fd.x_axis_format !== undefined) {
|
||||
xAxisFormatter = timeFormatFactory(fd.x_axis_format);
|
||||
chart.xAxis.tickFormat(xAxisFormatter);
|
||||
}
|
||||
|
||||
const isTimeSeries = timeStampFormats.indexOf(fd.x_axis_format) > -1;
|
||||
// if x axis format is a date format, rotate label 90 degrees
|
||||
@ -334,28 +323,26 @@ function nvd3Vis(slice, payload) {
|
||||
chart.xAxis.rotateLabels(45);
|
||||
}
|
||||
|
||||
if (chart.hasOwnProperty('x2Axis')) {
|
||||
let xAxisFormatter = d3FormatPreset(fd.x_axis_format);
|
||||
if (isTimeSeries && fd.x_axis_format) {
|
||||
xAxisFormatter = d3TimeFormatPreset(fd.x_axis_format);
|
||||
}
|
||||
if (chart.x2Axis && chart.x2Axis.tickFormat) {
|
||||
chart.x2Axis.tickFormat(xAxisFormatter);
|
||||
height += 30;
|
||||
}
|
||||
|
||||
if (vizType === 'bubble') {
|
||||
chart.xAxis.tickFormat(d3.format('.3s'));
|
||||
} else if (fd.x_axis_format === 'smart_date') {
|
||||
chart.xAxis.tickFormat(formatDate);
|
||||
} else if (fd.x_axis_format !== undefined) {
|
||||
chart.xAxis.tickFormat(timeFormatFactory(fd.x_axis_format));
|
||||
}
|
||||
if (chart.yAxis !== undefined) {
|
||||
chart.yAxis.tickFormat(d3.format('.3s'));
|
||||
if (chart.xAxis && chart.xAxis.tickFormat) {
|
||||
chart.xAxis.tickFormat(xAxisFormatter);
|
||||
}
|
||||
|
||||
if (fd.y_axis_format && chart.yAxis) {
|
||||
chart.yAxis.tickFormat(d3.format(fd.y_axis_format));
|
||||
if (chart.y2Axis !== undefined) {
|
||||
chart.y2Axis.tickFormat(d3.format(fd.y_axis_format));
|
||||
}
|
||||
const yAxisFormatter = d3FormatPreset(fd.y_axis_format);
|
||||
if (chart.yAxis && chart.yAxis.tickFormat) {
|
||||
chart.yAxis.tickFormat(yAxisFormatter);
|
||||
}
|
||||
if (chart.y2Axis && chart.y2Axis.tickFormat) {
|
||||
chart.y2Axis.tickFormat(yAxisFormatter);
|
||||
}
|
||||
|
||||
if (vizType !== 'bullet') {
|
||||
chart.color(d => category21(d[colorKey]));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user