diff --git a/superset/assets/.eslintrc b/superset/assets/.eslintrc index 9f93351a2c18e..955a1e12a07e1 100644 --- a/superset/assets/.eslintrc +++ b/superset/assets/.eslintrc @@ -5,6 +5,9 @@ "experimentalObjectRestSpread": true } }, + "globals": { + "document": true, + }, "rules": { "prefer-template": 0, "new-cap": 0, @@ -14,8 +17,5 @@ "func-names": 0, "react/jsx-no-bind": 0, "no-confusing-arrow": 0, - }, - "globals": { - "document": true, } } diff --git a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx index c914cf55f8933..1053222dac4a4 100644 --- a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx +++ b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx @@ -57,22 +57,19 @@ class ChartContainer extends React.PureComponent { } componentDidUpdate(prevProps) { - if (this.shouldRenderViz(prevProps)) { + if ( + ( + prevProps.queryResponse !== this.props.queryResponse || + prevProps.height !== this.props.height || + this.props.triggerRender + ) && !this.props.queryResponse.error + && this.props.chartStatus !== 'failed' + && this.props.chartStatus !== 'stopped' + ) { this.renderViz(); } } - shouldRenderViz(prevProps) { - const hasHeightChanged = prevProps.height !== this.props.height; - const hasQueryChanged = prevProps.queryResponse !== this.props.queryResponse; - const hasErrors = this.props.queryResponse && this.props.queryResponse.error; - - return (hasQueryChanged || hasHeightChanged || this.props.triggerRender) - && !hasErrors - && this.props.chartStatus !== 'failed' - && this.props.chartStatus !== 'stopped'; - } - getMockedSliceObject() { const props = this.props; const getHeight = () => { @@ -105,7 +102,7 @@ class ChartContainer extends React.PureComponent { height: getHeight, - render_template: function (s) { + render_template: (s) => { const context = { width: this.width, height: this.height, diff --git a/superset/assets/javascripts/modules/utils.js b/superset/assets/javascripts/modules/utils.js index 92bc931e4f612..9edaabf9656c4 100644 --- a/superset/assets/javascripts/modules/utils.js +++ b/superset/assets/javascripts/modules/utils.js @@ -189,14 +189,6 @@ export function customizeToolTip(chart, xAxisFormatter, yAxisFormatters) { }); } -export function getTextWidth(text, fontDetails) { - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - context.font = fontDetails; - const metrics = context.measureText(text); - return metrics.width; -} - export function initJQueryAjaxCSRF() { // Works in conjunction with a Flask-WTF token as described here: // http://flask-wtf.readthedocs.io/en/stable/csrf.html#javascript-requests diff --git a/superset/assets/visualizations/big_number.css b/superset/assets/visualizations/big_number.css index db34a45a40945..e490395914b30 100644 --- a/superset/assets/visualizations/big_number.css +++ b/superset/assets/visualizations/big_number.css @@ -2,18 +2,18 @@ .big_number_total g.axis text { font-size: 10px; font-weight: normal; - color: #333333; - fill: #333333; + color: gray; + fill: gray; text-anchor: middle; alignment-baseline: middle; - font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: none; } .big_number text.big, .big_number_total text.big{ - stroke: #333333; + stroke: black; text-anchor: middle; - fill: #333333; + fill: black; } .big_number g.tick line, @@ -25,16 +25,6 @@ .big_number .domain, .big_number_total .domain{ fill: none; - stroke: #333333; + stroke: black; + stroke-width: 1; } - -.line-tooltip { - position: absolute; - text-align: left; - padding: 10px; - background: #ffffff; - border: 1px solid #ccc; - border-radius: 2px; - pointer-events: none; -} - diff --git a/superset/assets/visualizations/big_number.js b/superset/assets/visualizations/big_number.js index b64ed59548d75..af943a2facdb5 100644 --- a/superset/assets/visualizations/big_number.js +++ b/superset/assets/visualizations/big_number.js @@ -1,18 +1,8 @@ import d3 from 'd3'; import { formatDate } from '../javascripts/modules/dates'; -import { getTextWidth } from '../javascripts/modules/utils'; require('./big_number.css'); -function getNumTicks(data, slice, margin) { - let numTicks = parseInt((slice.width() - margin) * 0.01, 10); - // if numTicks is greater than the total num of data points, show all data points - if (numTicks > data.length) { - numTicks = data.length; - } - return numTicks; -} - function bigNumberVis(slice, payload) { const div = d3.select(slice.selector); // Define the percentage bounds that define color from red to green @@ -48,77 +38,70 @@ function bigNumberVis(slice, payload) { } const dateExt = d3.extent(data, (d) => d[0]); const valueExt = d3.extent(data, (d) => d[1]); - const yAxisLabelWidths = valueExt.map(value => getTextWidth(f(value), '10px Roboto')); - const yAxisMaxWidth = Math.max(...yAxisLabelWidths); - let margin = yAxisMaxWidth + (yAxisMaxWidth / 2); - // make sure margin is minimum 30px, for the case when the y axix label is very small. - if (margin < 30) margin = 30; + + const margin = 20; const scaleX = d3.time.scale.utc().domain(dateExt).range([margin, width - margin]); const scaleY = d3.scale.linear().domain(valueExt).range([height - (margin), margin]); const colorRange = [d3.hsl(0, 1, 0.3), d3.hsl(120, 1, 0.3)]; const scaleColor = d3.scale - .linear().domain([-1, 1]) - .interpolate(d3.interpolateHsl) - .range(colorRange) - .clamp(true); + .linear().domain([-1, 1]) + .interpolate(d3.interpolateHsl) + .range(colorRange) + .clamp(true); const line = d3.svg.line() - .x(d => scaleX(d[0])) - .y(d => scaleY(d[1])) - .interpolate('cardinal'); + .x(function (d) { + return scaleX(d[0]); + }) + .y(function (d) { + return scaleY(d[1]); + }) + .interpolate('basis'); let y = height / 2; let g = svg.append('g'); - - const formattedNumber = f(v); - // Printing big number - let bigNumberFontSize = (width / formattedNumber.length) * 1.3; - if (formattedNumber.length === 1) { - bigNumberFontSize = (width / 2) * 1.3; - } - g.append('g') - .attr('class', 'digits') - .attr('opacity', 1) - .append('text') - .attr('x', width / 2) - .attr('y', y) - .attr('class', 'big') - .attr('alignment-baseline', 'middle') - .attr('id', 'bigNumber') - .style('font-weight', 'bold') - .style('cursor', 'pointer') - .text(formattedNumber) - .attr('font-family', 'Roboto') - .attr('font-size', bigNumberFontSize) - .style('text-anchor', 'middle') - .attr('fill', 'black'); + g.append('g').attr('class', 'digits') + .attr('opacity', 1) + .append('text') + .attr('x', width / 2) + .attr('y', y) + .attr('class', 'big') + .attr('alignment-baseline', 'middle') + .attr('id', 'bigNumber') + .style('font-weight', 'bold') + .style('cursor', 'pointer') + .text(f(v)) + .style('font-size', d3.min([height, width]) / 3.5) + .style('text-anchor', 'middle') + .attr('fill', 'black'); // Printing big number subheader text - if (json.subheader) { - const fontSize = (width / json.subheader.length) * 1.5; + if (json.subheader !== null) { g.append('text') - .attr('x', width / 2) - .attr('y', (height / 16) * 12) - .text(json.subheader) - .attr('id', 'subheader_text') - .attr('font-family', 'Roboto') - .attr('font-size', fontSize) - .style('text-anchor', 'middle'); + .attr('x', width / 2) + .attr('y', (height / 16) * 12) + .text(json.subheader) + .attr('id', 'subheader_text') + .style('font-size', d3.min([height, width]) / 8) + .style('text-anchor', 'middle'); } if (fd.viz_type === 'big_number') { // Drawing trend line + g.append('path') - .attr('d', () => line(data)) - .attr('stroke-width', 5) - .attr('opacity', 0.5) - .attr('fill', 'none') - .attr('stroke-linecap', 'round') - .attr('stroke', 'grey'); + .attr('d', function () { + return line(data); + }) + .attr('stroke-width', 5) + .attr('opacity', 0.5) + .attr('fill', 'none') + .attr('stroke-linecap', 'round') + .attr('stroke', 'grey'); g = svg.append('g') - .attr('class', 'digits') - .attr('opacity', 1); + .attr('class', 'digits') + .attr('opacity', 1); if (vCompare !== null) { y = (height / 8) * 3; @@ -129,116 +112,71 @@ function bigNumberVis(slice, payload) { // Printing compare % if (vCompare) { g.append('text') - .attr('x', width / 2) - .attr('y', (height / 16) * 12) - .text(fp(vCompare) + json.compare_suffix) - .style('font-size', d3.min([height, width]) / 8) - .style('text-anchor', 'middle') - .attr('fill', c) - .attr('stroke', c); + .attr('x', width / 2) + .attr('y', (height / 16) * 12) + .text(fp(vCompare) + json.compare_suffix) + .style('font-size', d3.min([height, width]) / 8) + .style('text-anchor', 'middle') + .attr('fill', c) + .attr('stroke', c); } - // axes const gAxis = svg.append('g').attr('class', 'axis').attr('opacity', 0); g = gAxis.append('g'); - const minMaxTickValues = scaleX.domain(); - // prepend the min value, and append the max value to the list of tick values - const tickValues = - [minMaxTickValues[0]] - .concat(scaleX.ticks(getNumTicks(data, slice, margin))) - .concat([minMaxTickValues[1]]); const xAxis = d3.svg.axis() - .scale(scaleX) - .orient('bottom') - .tickValues(tickValues) - .tickFormat(formatDate); + .scale(scaleX) + .orient('bottom') + .ticks(4) + .tickFormat(formatDate); g.call(xAxis); - g.attr('transform', 'translate(0,' + (height - margin) + ')').attr('class', 'xAxis'); + g.attr('transform', 'translate(0,' + (height - margin) + ')'); - g = gAxis.append('g').attr('transform', `translate(${margin}, 0)`).attr('class', 'yAxis'); + g = gAxis.append('g').attr('transform', 'translate(' + (width - margin) + ',0)'); const yAxis = d3.svg.axis() - .scale(scaleY) - .orient('left') - .tickFormat(d3.format(fd.y_axis_format)) - .tickValues(valueExt); + .scale(scaleY) + .orient('left') + .tickFormat(d3.format(fd.y_axis_format)) + .tickValues(valueExt); g.call(yAxis); g.selectAll('text') - .style('text-anchor', 'end') - .attr('y', '-7') - .attr('x', '-4'); - - // Define the div for the tooltip - const tooltipEl = - d3.select('body') - .append('div') - .attr('class', 'line-tooltip') - .attr('width', 200) - .attr('height', 200) - .style('opacity', 0); - - const renderTooltip = (d) => { - const date = formatDate(d[0]); - const value = f(d[1]); - return ` -