diff --git a/client/app/visualizations/chart/chart-editor.html b/client/app/visualizations/chart/chart-editor.html index 202b92f0a..2d88d33c5 100644 --- a/client/app/visualizations/chart/chart-editor.html +++ b/client/app/visualizations/chart/chart-editor.html @@ -12,6 +12,9 @@
  • Series
  • +
  • + Colors +
  • Data Labels
  • @@ -210,15 +213,15 @@
    - +
    - +
    - +
    @@ -226,40 +229,28 @@
    - - - + + + - - + - - - - -
    zIndexLeft Y AxisRight Y AxiszIndexLeft Y AxisRight Y Axis LabelColorTypeType
    + + + - - - - - - - - - - +
    @@ -281,6 +272,52 @@
    +
    + + + + + + + +
    +
    {{ name }}
    +
    + + + + + + + + + +
    +
    + +
    + + + + + + + +
    +
    {{ name }}
    +
    + + + + + + + + + +
    +
    +
    - \ No newline at end of file + diff --git a/client/app/visualizations/chart/index.js b/client/app/visualizations/chart/index.js index 879750122..4946aeb43 100644 --- a/client/app/visualizations/chart/index.js +++ b/client/app/visualizations/chart/index.js @@ -14,6 +14,7 @@ const DEFAULT_OPTIONS = { error_y: { type: 'data', visible: true }, series: { stacking: null, error_y: { type: 'data', visible: true } }, seriesOptions: {}, + valuesOptions: {}, columnMapping: {}, // showDataLabels: false, // depends on chart type @@ -156,7 +157,8 @@ function ChartEditor(ColorPalette, clientConfig) { } function refreshSeries() { - const seriesNames = map(scope.queryResult.getChartData(scope.options.columnMapping), i => i.name); + const chartData = scope.queryResult.getChartData(scope.options.columnMapping); + const seriesNames = map(chartData, i => i.name); const existing = keys(scope.options.seriesOptions); each(difference(seriesNames, existing), (name) => { scope.options.seriesOptions[name] = { @@ -169,6 +171,31 @@ function ChartEditor(ColorPalette, clientConfig) { scope.form.seriesList = without(scope.form.seriesList, name); delete scope.options.seriesOptions[name]; }); + + if (scope.options.globalSeriesType === 'pie') { + const uniqueValuesNames = new Set(); + each(chartData, (series) => { + each(series.data, (row) => { + uniqueValuesNames.add(row.x); + }); + }); + const valuesNames = []; + uniqueValuesNames.forEach(v => valuesNames.push(v)); + + // initialize newly added values + const newValues = difference(valuesNames, keys(scope.options.valuesOptions)); + each(newValues, (name) => { + scope.options.valuesOptions[name] = {}; + scope.form.valuesList.push(name); + }); + // remove settings for values that are no longer available + each(keys(scope.options.valuesOptions), (name) => { + if (valuesNames.indexOf(name) === -1) { + delete scope.options.valuesOptions[name]; + } + }); + scope.form.valuesList = intersection(scope.form.valuesList, valuesNames); + } } function setColumnRole(role, column) { @@ -200,6 +227,7 @@ function ChartEditor(ColorPalette, clientConfig) { yAxisColumns: [], seriesList: sortBy(keys(scope.options.seriesOptions), name => scope.options.seriesOptions[name].zIndex), + valuesList: keys(scope.options.valuesOptions), }; scope.$watchCollection('form.seriesList', (value) => { @@ -273,7 +301,7 @@ function ChartEditor(ColorPalette, clientConfig) { }); } - scope.$watch('options', () => { + function setOptionsDefaults() { if (scope.options) { // For existing visualization - set default options defaults(scope.options, extend({}, DEFAULT_OPTIONS, { @@ -281,7 +309,9 @@ function ChartEditor(ColorPalette, clientConfig) { dateTimeFormat: clientConfig.dateTimeFormat, })); } - }); + } + setOptionsDefaults(); + scope.$watch('options', setOptionsDefaults); scope.templateHint = `
    Use special names to access additional properties:
    diff --git a/client/app/visualizations/chart/plotly/utils.js b/client/app/visualizations/chart/plotly/utils.js index c118f49f7..537b7c7fe 100644 --- a/client/app/visualizations/chart/plotly/utils.js +++ b/client/app/visualizations/chart/plotly/utils.js @@ -3,6 +3,7 @@ import { each, values, sortBy, identity, filter, map, extend, reduce, } from 'lodash'; import moment from 'moment'; +import d3 from 'd3'; import { createFormatter, formatSimpleTemplate } from '@/lib/value-format'; // The following colors will be used if you pick "Automatic" color. @@ -227,6 +228,15 @@ function preparePieData(seriesList, options) { const hoverinfo = getPieHoverInfoPattern(options); + // we will use this to assign colors for values that have not explicitly set color + const getDefaultColor = d3.scale.ordinal().domain([]).range(ColorPaletteArray); + const valuesColors = {}; + each(options.valuesOptions, (item, key) => { + if (isString(item.color) && (item.color !== '')) { + valuesColors[key] = item.color; + } + }); + return map(seriesList, (serie, index) => { const xPosition = (index % cellsInRow) * cellWidth; const yPosition = Math.floor(index / cellsInRow) * cellHeight; @@ -255,7 +265,9 @@ function preparePieData(seriesList, options) { labels: map(serie.data, row => (hasX ? normalizeValue(row.x) : `Slice ${index}`)), type: 'pie', hole: 0.4, - marker: { colors: ColorPaletteArray }, + marker: { + colors: map(serie.data, row => valuesColors[row.x] || getDefaultColor(row.x)), + }, hoverinfo, text: [], textinfo: options.showDataLabels ? 'percent' : 'none',