mirror of https://github.com/apache/superset.git
feat(superset-ui-core): add feature flag for the analogous colors (#19987)
* feat(superset-ui-core): add feature flag for the color analogous generator * fix: test
This commit is contained in:
parent
766f737728
commit
80b5578680
|
@ -24,6 +24,7 @@ import { ColorsLookup } from './types';
|
||||||
import stringifyAndTrim from './stringifyAndTrim';
|
import stringifyAndTrim from './stringifyAndTrim';
|
||||||
import getSharedLabelColor from './SharedLabelColorSingleton';
|
import getSharedLabelColor from './SharedLabelColorSingleton';
|
||||||
import { getAnalogousColors } from './utils';
|
import { getAnalogousColors } from './utils';
|
||||||
|
import { FeatureFlag, isFeatureEnabled } from '../utils';
|
||||||
|
|
||||||
// Use type augmentation to correct the fact that
|
// Use type augmentation to correct the fact that
|
||||||
// an instance of CategoricalScale is also a function
|
// an instance of CategoricalScale is also a function
|
||||||
|
@ -79,13 +80,15 @@ class CategoricalColorScale extends ExtensibleFunction {
|
||||||
return forcedColor;
|
return forcedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const multiple = Math.floor(
|
if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
|
||||||
this.domain().length / this.originColors.length,
|
const multiple = Math.floor(
|
||||||
);
|
this.domain().length / this.originColors.length,
|
||||||
if (multiple > this.multiple) {
|
);
|
||||||
this.multiple = multiple;
|
if (multiple > this.multiple) {
|
||||||
const newRange = getAnalogousColors(this.originColors, multiple);
|
this.multiple = multiple;
|
||||||
this.range(this.originColors.concat(newRange));
|
const newRange = getAnalogousColors(this.originColors, multiple);
|
||||||
|
this.range(this.originColors.concat(newRange));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = this.scale(cleanedValue);
|
const color = this.scale(cleanedValue);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CategoricalColorNamespace } from '.';
|
import { CategoricalColorNamespace } from '.';
|
||||||
import makeSingleton from '../utils/makeSingleton';
|
import { FeatureFlag, isFeatureEnabled, makeSingleton } from '../utils';
|
||||||
import { getAnalogousColors } from './utils';
|
import { getAnalogousColors } from './utils';
|
||||||
|
|
||||||
export class SharedLabelColor {
|
export class SharedLabelColor {
|
||||||
|
@ -37,25 +37,39 @@ export class SharedLabelColor {
|
||||||
if (colorScheme) {
|
if (colorScheme) {
|
||||||
const categoricalNamespace =
|
const categoricalNamespace =
|
||||||
CategoricalColorNamespace.getNamespace(colorNamespace);
|
CategoricalColorNamespace.getNamespace(colorNamespace);
|
||||||
const colors = categoricalNamespace.getScale(colorScheme).range();
|
|
||||||
const sharedLabels = this.getSharedLabels();
|
const sharedLabels = this.getSharedLabels();
|
||||||
let generatedColors: string[] = [];
|
let generatedColors: string[] = [];
|
||||||
let sharedLabelMap;
|
let sharedLabelMap;
|
||||||
|
|
||||||
if (sharedLabels.length) {
|
if (sharedLabels.length) {
|
||||||
const multiple = Math.ceil(sharedLabels.length / colors.length);
|
const colorScale = categoricalNamespace.getScale(colorScheme);
|
||||||
generatedColors = getAnalogousColors(colors, multiple);
|
const colors = colorScale.range();
|
||||||
sharedLabelMap = sharedLabels.reduce(
|
if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
|
||||||
(res, label, index) => ({
|
const multiple = Math.ceil(sharedLabels.length / colors.length);
|
||||||
...res,
|
generatedColors = getAnalogousColors(colors, multiple);
|
||||||
[label.toString()]: generatedColors[index],
|
sharedLabelMap = sharedLabels.reduce(
|
||||||
}),
|
(res, label, index) => ({
|
||||||
{},
|
...res,
|
||||||
);
|
[label.toString()]: generatedColors[index],
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// reverse colors to reduce color conflicts
|
||||||
|
colorScale.range(colors.reverse());
|
||||||
|
sharedLabelMap = sharedLabels.reduce(
|
||||||
|
(res, label) => ({
|
||||||
|
...res,
|
||||||
|
[label.toString()]: colorScale(label),
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelMap = Object.keys(this.sliceLabelColorMap).reduce(
|
const labelMap = Object.keys(this.sliceLabelColorMap).reduce(
|
||||||
(res, sliceId) => {
|
(res, sliceId) => {
|
||||||
|
// get new color scale instance
|
||||||
const colorScale = categoricalNamespace.getScale(colorScheme);
|
const colorScale = categoricalNamespace.getScale(colorScheme);
|
||||||
return {
|
return {
|
||||||
...res,
|
...res,
|
||||||
|
|
|
@ -54,6 +54,7 @@ export enum FeatureFlag {
|
||||||
ALLOW_FULL_CSV_EXPORT = 'ALLOW_FULL_CSV_EXPORT',
|
ALLOW_FULL_CSV_EXPORT = 'ALLOW_FULL_CSV_EXPORT',
|
||||||
UX_BETA = 'UX_BETA',
|
UX_BETA = 'UX_BETA',
|
||||||
GENERIC_CHART_AXES = 'GENERIC_CHART_AXES',
|
GENERIC_CHART_AXES = 'GENERIC_CHART_AXES',
|
||||||
|
USE_ANALAGOUS_COLORS = 'USE_ANALAGOUS_COLORS',
|
||||||
}
|
}
|
||||||
export type ScheduleQueriesProps = {
|
export type ScheduleQueriesProps = {
|
||||||
JSONSCHEMA: {
|
JSONSCHEMA: {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ScaleOrdinal } from 'd3-scale';
|
import { ScaleOrdinal } from 'd3-scale';
|
||||||
import { CategoricalColorScale } from '@superset-ui/core';
|
import { CategoricalColorScale, FeatureFlag } from '@superset-ui/core';
|
||||||
|
|
||||||
describe('CategoricalColorScale', () => {
|
describe('CategoricalColorScale', () => {
|
||||||
it('exists', () => {
|
it('exists', () => {
|
||||||
|
@ -62,7 +62,36 @@ describe('CategoricalColorScale', () => {
|
||||||
expect(c2).not.toBe(c3);
|
expect(c2).not.toBe(c3);
|
||||||
expect(c3).not.toBe(c1);
|
expect(c3).not.toBe(c1);
|
||||||
});
|
});
|
||||||
|
it('recycles colors when number of items exceed available colors', () => {
|
||||||
|
window.featureFlags = {
|
||||||
|
[FeatureFlag.USE_ANALAGOUS_COLORS]: false,
|
||||||
|
};
|
||||||
|
const colorSet: { [key: string]: number } = {};
|
||||||
|
const scale = new CategoricalColorScale(['blue', 'red', 'green']);
|
||||||
|
const colors = [
|
||||||
|
scale.getColor('pig'),
|
||||||
|
scale.getColor('horse'),
|
||||||
|
scale.getColor('cat'),
|
||||||
|
scale.getColor('cow'),
|
||||||
|
scale.getColor('donkey'),
|
||||||
|
scale.getColor('goat'),
|
||||||
|
];
|
||||||
|
colors.forEach(color => {
|
||||||
|
if (colorSet[color]) {
|
||||||
|
colorSet[color] += 1;
|
||||||
|
} else {
|
||||||
|
colorSet[color] = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(Object.keys(colorSet)).toHaveLength(3);
|
||||||
|
['blue', 'red', 'green'].forEach(color => {
|
||||||
|
expect(colorSet[color]).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
it('get analogous colors when number of items exceed available colors', () => {
|
it('get analogous colors when number of items exceed available colors', () => {
|
||||||
|
window.featureFlags = {
|
||||||
|
[FeatureFlag.USE_ANALAGOUS_COLORS]: true,
|
||||||
|
};
|
||||||
const scale = new CategoricalColorScale(['blue', 'red', 'green']);
|
const scale = new CategoricalColorScale(['blue', 'red', 'green']);
|
||||||
scale.getColor('pig');
|
scale.getColor('pig');
|
||||||
scale.getColor('horse');
|
scale.getColor('horse');
|
||||||
|
|
|
@ -425,6 +425,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = {
|
||||||
"UX_BETA": False,
|
"UX_BETA": False,
|
||||||
"GENERIC_CHART_AXES": False,
|
"GENERIC_CHART_AXES": False,
|
||||||
"ALLOW_ADHOC_SUBQUERY": False,
|
"ALLOW_ADHOC_SUBQUERY": False,
|
||||||
|
"USE_ANALAGOUS_COLORS": True,
|
||||||
# Apply RLS rules to SQL Lab queries. This requires parsing and manipulating the
|
# Apply RLS rules to SQL Lab queries. This requires parsing and manipulating the
|
||||||
# query, and might break queries and/or allow users to bypass RLS. Use with care!
|
# query, and might break queries and/or allow users to bypass RLS. Use with care!
|
||||||
"RLS_IN_SQLLAB": False,
|
"RLS_IN_SQLLAB": False,
|
||||||
|
|
Loading…
Reference in New Issue