Allow users to specify label->color mapping (#3879)
Users can define `label_colors` in a dashboard's JSON metadata that enforces a label to color mapping. This also makes the function that maps labels to colors case insensitive.
This commit is contained in:
parent
a84bd5225c
commit
a82bb588f4
16
docs/faq.rst
16
docs/faq.rst
|
|
@ -221,3 +221,19 @@ When adding columns to a table, you can have Superset detect and merge the
|
|||
new columns in by using the "Refresh Metadata" action in the
|
||||
``Source -> Tables`` page. Simply check the box next to the tables
|
||||
you want the schema refreshed, and click ``Actions -> Refresh Metadata``.
|
||||
|
||||
Is there a way to force the use specific colors?
|
||||
------------------------------------------------
|
||||
|
||||
It is possible on a per-dashboard basis by providing a mapping of
|
||||
labels to colors in the ``JSON Metadata`` attribute using the
|
||||
``label_colors`` key.
|
||||
|
||||
..code::
|
||||
|
||||
{
|
||||
"label_colors": {
|
||||
"Girls": "#FF69B4",
|
||||
"Boys": "#ADD8E6"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import * as actions from './actions';
|
|||
import { getParam } from '../modules/utils';
|
||||
import { alterInArr, removeFromArr } from '../reduxUtils';
|
||||
import { applyDefaultFormData } from '../explore/stores/store';
|
||||
import { getColorFromScheme } from '../modules/colors';
|
||||
|
||||
export function getInitialState(bootstrapData) {
|
||||
const { user_id, datasources, common } = bootstrapData;
|
||||
|
|
@ -25,6 +26,15 @@ export function getInitialState(bootstrapData) {
|
|||
//
|
||||
}
|
||||
|
||||
// Priming the color palette with user's label-color mapping provided in
|
||||
// the dashboard's JSON metadata
|
||||
if (dashboard.metadata && dashboard.metadata.label_colors) {
|
||||
const colorMap = dashboard.metadata.label_colors;
|
||||
for (const label in colorMap) {
|
||||
getColorFromScheme(label, null, colorMap[label]);
|
||||
}
|
||||
}
|
||||
|
||||
dashboard.posDict = {};
|
||||
dashboard.layout = [];
|
||||
if (dashboard.position_json) {
|
||||
|
|
|
|||
|
|
@ -103,17 +103,36 @@ export const spectrums = {
|
|||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a color from a scheme specific palette (scheme)
|
||||
* The function cycles through the palette while memoizing labels
|
||||
* association to colors. If the function is called twice with the
|
||||
* same string, it will return the same color.
|
||||
*
|
||||
* @param {string} s - The label for which we want to get a color
|
||||
* @param {string} scheme - The palette name, or "scheme"
|
||||
* @param {string} forcedColor - A color that the caller wants to
|
||||
forcibly associate to a label.
|
||||
*/
|
||||
export const getColorFromScheme = (function () {
|
||||
// Color factory
|
||||
const seen = {};
|
||||
return function (s, scheme) {
|
||||
const forcedColors = {};
|
||||
return function (s, scheme, forcedColor) {
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
const selectedScheme = scheme ? ALL_COLOR_SCHEMES[scheme] : ALL_COLOR_SCHEMES.bnbColors;
|
||||
let stringifyS = String(s);
|
||||
let stringifyS = String(s).toLowerCase();
|
||||
// next line is for superset series that should have the same color
|
||||
stringifyS = stringifyS.replace('---', '');
|
||||
|
||||
if (forcedColor && !forcedColors[stringifyS]) {
|
||||
forcedColors[stringifyS] = forcedColor;
|
||||
}
|
||||
if (forcedColors[stringifyS]) {
|
||||
return forcedColors[stringifyS];
|
||||
}
|
||||
|
||||
if (seen[selectedScheme] === undefined) {
|
||||
seen[selectedScheme] = {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ describe('colors', () => {
|
|||
const color1 = getColorFromScheme('CA');
|
||||
expect(color1).to.equal(ALL_COLOR_SCHEMES.bnbColors[0]);
|
||||
});
|
||||
it('series with same scheme should have the same color', () => {
|
||||
it('getColorFromScheme series with same scheme should have the same color', () => {
|
||||
const color1 = getColorFromScheme('CA', 'bnbColors');
|
||||
const color2 = getColorFromScheme('CA', 'googleCategory20c');
|
||||
const color3 = getColorFromScheme('CA', 'bnbColors');
|
||||
|
|
@ -19,7 +19,22 @@ describe('colors', () => {
|
|||
expect(color1).to.equal(color3);
|
||||
expect(color4).to.equal(ALL_COLOR_SCHEMES.bnbColors[1]);
|
||||
});
|
||||
it('getColorFromScheme forcing colors persists through calls', () => {
|
||||
expect(getColorFromScheme('boys', 'bnbColors', 'blue')).to.equal('blue');
|
||||
expect(getColorFromScheme('boys', 'bnbColors')).to.equal('blue');
|
||||
expect(getColorFromScheme('boys', 'googleCategory20c')).to.equal('blue');
|
||||
|
||||
expect(getColorFromScheme('girls', 'bnbColors', 'pink')).to.equal('pink');
|
||||
expect(getColorFromScheme('girls', 'bnbColors')).to.equal('pink');
|
||||
expect(getColorFromScheme('girls', 'googleCategory20c')).to.equal('pink');
|
||||
});
|
||||
it('getColorFromScheme is not case sensitive', () => {
|
||||
const c1 = getColorFromScheme('girls', 'bnbColors');
|
||||
const c2 = getColorFromScheme('Girls', 'bnbColors');
|
||||
const c3 = getColorFromScheme('GIRLS', 'bnbColors');
|
||||
expect(c1).to.equal(c2);
|
||||
expect(c3).to.equal(c2);
|
||||
});
|
||||
it('hexToRGB converts properly', () => {
|
||||
expect(hexToRGB('#FFFFFF')).to.have.same.members([255, 255, 255, 255]);
|
||||
expect(hexToRGB('#000000')).to.have.same.members([0, 0, 0, 255]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue