feat(color): color consistency enhancements (#21507)

This commit is contained in:
Stephen Liu 2022-10-17 17:01:20 +08:00 committed by GitHub
parent 7ec136fec2
commit 7a7181a244
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 237 additions and 245 deletions

View File

@ -51,7 +51,7 @@ class CategoricalColorScale extends ExtensibleFunction {
* @param {*} parentForcedColors optional parameter that comes from parent
* (usually CategoricalColorNamespace) and supersede this.forcedColors
*/
constructor(colors: string[], parentForcedColors?: ColorsLookup) {
constructor(colors: string[], parentForcedColors: ColorsLookup = {}) {
super((value: string, sliceId?: number) => this.getColor(value, sliceId));
this.originColors = colors;
@ -67,17 +67,11 @@ class CategoricalColorScale extends ExtensibleFunction {
const cleanedValue = stringifyAndTrim(value);
const sharedLabelColor = getSharedLabelColor();
const parentColor = this.parentForcedColors?.[cleanedValue];
if (parentColor) {
sharedLabelColor.addSlice(cleanedValue, parentColor, sliceId);
return parentColor;
}
const forcedColor = this.forcedColors[cleanedValue];
if (forcedColor) {
sharedLabelColor.addSlice(cleanedValue, forcedColor, sliceId);
return forcedColor;
}
// priority: parentForcedColors > forcedColors > labelColors
let color =
this.parentForcedColors?.[cleanedValue] ||
this.forcedColors?.[cleanedValue] ||
sharedLabelColor.getColorMap().get(cleanedValue);
if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
const multiple = Math.floor(
@ -89,8 +83,10 @@ class CategoricalColorScale extends ExtensibleFunction {
this.range(this.originColors.concat(newRange));
}
}
const color = this.scale(cleanedValue);
const newColor = this.scale(cleanedValue);
if (!color) {
color = newColor;
}
sharedLabelColor.addSlice(cleanedValue, color, sliceId);
return color;

View File

@ -18,113 +18,81 @@
*/
import { CategoricalColorNamespace } from '.';
import { FeatureFlag, isFeatureEnabled, makeSingleton } from '../utils';
import { getAnalogousColors } from './utils';
import { makeSingleton } from '../utils';
export enum SharedLabelColorSource {
dashboard,
explore,
}
export class SharedLabelColor {
sliceLabelColorMap: Record<number, Record<string, string | undefined>>;
sliceLabelMap: Map<number, string[]>;
colorMap: Map<string, string>;
source: SharedLabelColorSource;
constructor() {
// { sliceId1: { label1: color1 }, sliceId2: { label2: color2 } }
this.sliceLabelColorMap = {};
// { sliceId1: [label1, label2, ...], sliceId2: [label1, label2, ...] }
this.sliceLabelMap = new Map();
this.colorMap = new Map();
this.source = SharedLabelColorSource.dashboard;
}
getColorMap(
colorNamespace?: string,
colorScheme?: string,
updateColorScheme?: boolean,
) {
if (colorScheme) {
const categoricalNamespace =
CategoricalColorNamespace.getNamespace(colorNamespace);
const sharedLabels = this.getSharedLabels();
let generatedColors: string[] = [];
let sharedLabelMap;
updateColorMap(colorNamespace?: string, colorScheme?: string) {
const categoricalNamespace =
CategoricalColorNamespace.getNamespace(colorNamespace);
const newColorMap = new Map();
this.colorMap.clear();
this.sliceLabelMap.forEach(labels => {
const colorScale = categoricalNamespace.getScale(colorScheme);
labels.forEach(label => {
const newColor = colorScale(label);
newColorMap.set(label, newColor);
});
});
this.colorMap = newColorMap;
}
if (sharedLabels.length) {
const colorScale = categoricalNamespace.getScale(colorScheme);
const colors = colorScale.range();
if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
const multiple = Math.ceil(sharedLabels.length / colors.length);
generatedColors = getAnalogousColors(colors, multiple);
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(
(res, sliceId) => {
// get new color scale instance
const colorScale = categoricalNamespace.getScale(colorScheme);
return {
...res,
...Object.keys(this.sliceLabelColorMap[sliceId]).reduce(
(res, label) => ({
...res,
[label]: updateColorScheme
? colorScale(label)
: this.sliceLabelColorMap[sliceId][label],
}),
{},
),
};
},
{},
);
return {
...labelMap,
...sharedLabelMap,
};
}
return undefined;
getColorMap() {
return this.colorMap;
}
addSlice(label: string, color: string, sliceId?: number) {
if (!sliceId) return;
this.sliceLabelColorMap[sliceId] = {
...this.sliceLabelColorMap[sliceId],
[label]: color,
};
if (
this.source !== SharedLabelColorSource.dashboard ||
sliceId === undefined
)
return;
const labels = this.sliceLabelMap.get(sliceId) || [];
if (!labels.includes(label)) {
labels.push(label);
this.sliceLabelMap.set(sliceId, labels);
}
this.colorMap.set(label, color);
}
removeSlice(sliceId: number) {
delete this.sliceLabelColorMap[sliceId];
if (this.source !== SharedLabelColorSource.dashboard) return;
this.sliceLabelMap.delete(sliceId);
const newColorMap = new Map();
this.sliceLabelMap.forEach(labels => {
labels.forEach(label => {
newColorMap.set(label, this.colorMap.get(label));
});
});
this.colorMap = newColorMap;
}
reset() {
const copyColorMap = new Map(this.colorMap);
copyColorMap.forEach((_, label) => {
this.colorMap.set(label, '');
});
}
clear() {
this.sliceLabelColorMap = {};
}
getSharedLabels() {
const tempLabels = new Set<string>();
const result = new Set<string>();
Object.keys(this.sliceLabelColorMap).forEach(sliceId => {
const colorMap = this.sliceLabelColorMap[sliceId];
Object.keys(colorMap).forEach(label => {
if (tempLabels.has(label) && !result.has(label)) {
result.add(label);
} else {
tempLabels.add(label);
}
});
});
return [...result];
this.sliceLabelMap.clear();
this.colorMap.clear();
}
}

View File

@ -35,6 +35,7 @@ export * from './utils';
export {
default as getSharedLabelColor,
SharedLabelColor,
SharedLabelColorSource,
} from './SharedLabelColorSingleton';
export const BRAND_COLOR = '#00A699';

View File

@ -140,7 +140,7 @@ describe('CategoricalColorScale', () => {
expect(scale2.getColorMap()).toEqual({
cow: 'black',
pig: 'pink',
horse: 'blue',
horse: 'green',
});
});
});

View File

@ -23,6 +23,7 @@ import {
getCategoricalSchemeRegistry,
getSharedLabelColor,
SharedLabelColor,
SharedLabelColorSource,
} from '@superset-ui/core';
import { getAnalogousColors } from '../../src/color/utils';
@ -52,6 +53,7 @@ describe('SharedLabelColor', () => {
});
beforeEach(() => {
getSharedLabelColor().source = SharedLabelColorSource.dashboard;
getSharedLabelColor().clear();
});
@ -60,18 +62,48 @@ describe('SharedLabelColor', () => {
});
describe('.addSlice(value, color, sliceId)', () => {
it('should add to valueSliceMap when first adding label', () => {
it('should add to sliceLabelColorMap when first adding label', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
expect(sharedLabelColor.sliceLabelColorMap).toHaveProperty('1', {
a: 'red',
});
expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(true);
const labels = sharedLabelColor.sliceLabelMap.get(1);
expect(labels?.includes('a')).toEqual(true);
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ a: 'red' });
});
it('should add to sliceLabelColorMap when slice exist', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 1);
const labels = sharedLabelColor.sliceLabelMap.get(1);
expect(labels?.includes('b')).toEqual(true);
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ a: 'red', b: 'blue' });
});
it('should use last color if adding label repeatedly', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('b', 'blue', 1);
sharedLabelColor.addSlice('b', 'green', 1);
const labels = sharedLabelColor.sliceLabelMap.get(1);
expect(labels?.includes('b')).toEqual(true);
expect(labels?.length).toEqual(1);
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ b: 'green' });
});
it('should do nothing when source is not dashboard', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.source = SharedLabelColorSource.explore;
sharedLabelColor.addSlice('a', 'red');
expect(Object.fromEntries(sharedLabelColor.sliceLabelMap)).toEqual({});
});
it('should do nothing when sliceId is undefined', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red');
expect(sharedLabelColor.sliceLabelColorMap).toEqual({});
expect(Object.fromEntries(sharedLabelColor.sliceLabelMap)).toEqual({});
});
});
@ -80,55 +112,92 @@ describe('SharedLabelColor', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.removeSlice(1);
expect(sharedLabelColor.sliceLabelColorMap).toEqual({});
expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(false);
});
it('should update colorMap', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 2);
sharedLabelColor.removeSlice(1);
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ b: 'blue' });
});
it('should do nothing when source is not dashboard', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.source = SharedLabelColorSource.explore;
sharedLabelColor.removeSlice(1);
expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(true);
});
});
describe('.getColorMap(namespace, scheme, updateColorScheme)', () => {
it('should be undefined when scheme is undefined', () => {
describe('.updateColorMap(namespace, scheme)', () => {
it('should update color map', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'pink', 1);
sharedLabelColor.addSlice('b', 'green', 2);
sharedLabelColor.addSlice('c', 'blue', 2);
sharedLabelColor.updateColorMap('', 'testColors2');
const colorMap = sharedLabelColor.getColorMap();
expect(colorMap).toBeUndefined();
expect(Object.fromEntries(colorMap)).toEqual({
a: 'yellow',
b: 'yellow',
c: 'green',
});
});
it('should update color value if passing updateColorScheme', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 2);
const colorMap = sharedLabelColor.getColorMap('', 'testColors2', true);
expect(colorMap).toEqual({ a: 'yellow', b: 'yellow' });
});
it('should get origin color value if not pass updateColorScheme', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 2);
const colorMap = sharedLabelColor.getColorMap('', 'testColors');
expect(colorMap).toEqual({ a: 'red', b: 'blue' });
});
it('should use recycle colors if shared label exit', () => {
it('should use recycle colors', () => {
window.featureFlags = {
[FeatureFlag.USE_ANALAGOUS_COLORS]: false,
};
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('a', 'blue', 2);
const colorMap = sharedLabelColor.getColorMap('', 'testColors');
expect(colorMap).not.toEqual({});
sharedLabelColor.addSlice('b', 'blue', 2);
sharedLabelColor.addSlice('c', 'green', 3);
sharedLabelColor.addSlice('d', 'red', 4);
sharedLabelColor.updateColorMap('', 'testColors');
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).not.toEqual({});
expect(getAnalogousColors).not.toBeCalled();
});
it('should use analagous colors if shared label exit', () => {
it('should use analagous colors', () => {
window.featureFlags = {
[FeatureFlag.USE_ANALAGOUS_COLORS]: true,
};
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('a', 'blue', 2);
const colorMap = sharedLabelColor.getColorMap('', 'testColors');
expect(colorMap).not.toEqual({});
sharedLabelColor.addSlice('b', 'blue', 1);
sharedLabelColor.addSlice('c', 'green', 1);
sharedLabelColor.addSlice('d', 'red', 1);
sharedLabelColor.updateColorMap('', 'testColors');
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).not.toEqual({});
expect(getAnalogousColors).toBeCalled();
});
});
describe('.getColorMap()', () => {
it('should get color map', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 2);
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ a: 'red', b: 'blue' });
});
});
describe('.reset()', () => {
it('should reset color map', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.addSlice('b', 'blue', 2);
sharedLabelColor.reset();
const colorMap = sharedLabelColor.getColorMap();
expect(Object.fromEntries(colorMap)).toEqual({ a: '', b: '' });
});
});
});

View File

@ -72,11 +72,6 @@ export function removeSlice(sliceId) {
return { type: REMOVE_SLICE, sliceId };
}
export const RESET_SLICE = 'RESET_SLICE';
export function resetSlice() {
return { type: RESET_SLICE };
}
const FAVESTAR_BASE_URL = '/superset/favstar/Dashboard';
export const TOGGLE_FAVE_STAR = 'TOGGLE_FAVE_STAR';
export function toggleFaveStar(isStarred) {
@ -506,28 +501,6 @@ export function addSliceToDashboard(id, component) {
};
}
export function postAddSliceFromDashboard() {
return (dispatch, getState) => {
const {
dashboardInfo: { metadata },
dashboardState,
} = getState();
if (dashboardState?.updateSlice && dashboardState?.editMode) {
metadata.shared_label_colors = getSharedLabelColor().getColorMap(
metadata?.color_namespace,
metadata?.color_scheme,
);
dispatch(
dashboardInfoChanged({
metadata,
}),
);
dispatch(resetSlice());
}
};
}
export function removeSliceFromDashboard(id) {
return (dispatch, getState) => {
const sliceEntity = getState().sliceEntities.slices[id];
@ -537,20 +510,7 @@ export function removeSliceFromDashboard(id) {
dispatch(removeSlice(id));
dispatch(removeChart(id));
const {
dashboardInfo: { metadata },
} = getState();
getSharedLabelColor().removeSlice(id);
metadata.shared_label_colors = getSharedLabelColor().getColorMap(
metadata?.color_namespace,
metadata?.color_scheme,
);
dispatch(
dashboardInfoChanged({
metadata,
}),
);
};
}

View File

@ -120,6 +120,6 @@ describe('dashboardState actions', () => {
const removeFilter = dispatch.getCall(0).args[0];
removeFilter(dispatch, getState);
expect(dispatch.getCall(4).args[0].type).toBe(REMOVE_FILTER);
expect(dispatch.getCall(3).args[0].type).toBe(REMOVE_FILTER);
});
});

View File

@ -77,7 +77,8 @@ const createProps = () => ({
setEditMode: jest.fn(),
showBuilderPane: jest.fn(),
updateCss: jest.fn(),
setColorSchemeAndUnsavedChanges: jest.fn(),
setColorScheme: jest.fn(),
setUnsavedChanges: jest.fn(),
logEvent: jest.fn(),
setRefreshFrequency: jest.fn(),
hasUnsavedChanges: false,

View File

@ -75,7 +75,8 @@ const propTypes = {
customCss: PropTypes.string.isRequired,
colorNamespace: PropTypes.string,
colorScheme: PropTypes.string,
setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired,
setColorScheme: PropTypes.func.isRequired,
setUnsavedChanges: PropTypes.func.isRequired,
isStarred: PropTypes.bool.isRequired,
isPublished: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
@ -371,9 +372,8 @@ class Header extends React.PureComponent {
dashboardInfo?.metadata?.color_scheme || colorScheme;
const currentColorNamespace =
dashboardInfo?.metadata?.color_namespace || colorNamespace;
const currentSharedLabelColors = getSharedLabelColor().getColorMap(
currentColorNamespace,
currentColorScheme,
const currentSharedLabelColors = Object.fromEntries(
getSharedLabelColor().getColorMap(),
);
const data = {
@ -439,7 +439,8 @@ class Header extends React.PureComponent {
customCss,
colorNamespace,
dataMask,
setColorSchemeAndUnsavedChanges,
setColorScheme,
setUnsavedChanges,
colorScheme,
onUndo,
onRedo,
@ -480,6 +481,8 @@ class Header extends React.PureComponent {
const handleOnPropertiesChange = updates => {
const { dashboardInfoChanged, dashboardTitleChanged } = this.props;
setColorScheme(updates.colorScheme);
dashboardInfoChanged({
slug: updates.slug,
metadata: JSON.parse(updates.jsonMetadata || '{}'),
@ -488,7 +491,7 @@ class Header extends React.PureComponent {
owners: updates.owners,
roles: updates.roles,
});
setColorSchemeAndUnsavedChanges(updates.colorScheme);
setUnsavedChanges(true);
dashboardTitleChanged(updates.title);
};

View File

@ -68,7 +68,8 @@ export interface HeaderProps {
user: Object | undefined;
dashboardInfo: DashboardInfo;
dashboardTitle: string;
setColorSchemeAndUnsavedChanges: () => void;
setColorScheme: () => void;
setUnsavedChanges: () => void;
isStarred: boolean;
isPublished: boolean;
onChange: () => void;

View File

@ -309,7 +309,7 @@ test('submitting with onlyApply:false', async () => {
certificationDetails: 'Sample certification',
certifiedBy: 'John Doe',
colorScheme: 'supersetColors',
colorNamespace: '',
colorNamespace: undefined,
id: 26,
jsonMetadata: expect.anything(),
owners: [],

View File

@ -24,6 +24,7 @@ import Button from 'src/components/Button';
import { AntdForm, AsyncSelect, Col, Row } from 'src/components';
import rison from 'rison';
import {
CategoricalColorNamespace,
ensureIsArray,
getCategoricalSchemeRegistry,
getSharedLabelColor,
@ -57,7 +58,6 @@ type PropertiesModalProps = {
show?: boolean;
onHide?: () => void;
colorScheme?: string;
setColorSchemeAndUnsavedChanges?: () => void;
onSubmit?: (params: Record<string, any>) => void;
addSuccessToast: (message: string) => void;
addDangerToast: (message: string) => void;
@ -181,9 +181,7 @@ const PropertiesModal = ({
}
const metaDataCopy = { ...metadata };
if (metaDataCopy?.shared_label_colors) {
delete metaDataCopy.shared_label_colors;
}
delete metaDataCopy.shared_label_colors;
delete metaDataCopy.color_scheme_domain;
@ -268,7 +266,7 @@ const PropertiesModal = ({
};
const onColorSchemeChange = (
colorScheme?: string,
colorScheme = '',
{ updateMetadata = true } = {},
) => {
// check that color_scheme is valid
@ -319,7 +317,7 @@ const PropertiesModal = ({
// color scheme in json metadata has precedence over selection
currentColorScheme = metadata?.color_scheme || colorScheme;
colorNamespace = metadata?.color_namespace || '';
colorNamespace = metadata?.color_namespace;
// filter shared_label_color from user input
if (metadata?.shared_label_colors) {
@ -329,16 +327,20 @@ const PropertiesModal = ({
delete metadata.color_scheme_domain;
}
metadata.shared_label_colors = getSharedLabelColor().getColorMap(
colorNamespace,
currentColorScheme,
true,
);
if (metadata?.color_scheme) {
const sharedLabelColor = getSharedLabelColor();
const categoricalNamespace =
CategoricalColorNamespace.getNamespace(colorNamespace);
categoricalNamespace.resetColors();
if (currentColorScheme) {
sharedLabelColor.updateColorMap(colorNamespace, currentColorScheme);
metadata.shared_label_colors = Object.fromEntries(
sharedLabelColor.getColorMap(),
);
metadata.color_scheme_domain =
categoricalSchemeRegistry.get(colorScheme)?.colors || [];
} else {
sharedLabelColor.reset();
metadata.shared_label_colors = {};
metadata.color_scheme_domain = [];
}
@ -367,9 +369,9 @@ const PropertiesModal = ({
...moreOnSubmitProps,
};
if (onlyApply) {
addSuccessToast(t('Dashboard properties updated'));
onSubmit(onSubmitProps);
onHide();
addSuccessToast(t('Dashboard properties updated'));
} else {
SupersetClient.put({
endpoint: `/api/v1/dashboard/${dashboardId}`,
@ -385,9 +387,9 @@ const PropertiesModal = ({
...morePutProps,
}),
}).then(() => {
addSuccessToast(t('The dashboard has been saved'));
onSubmit(onSubmitProps);
onHide();
addSuccessToast(t('The dashboard has been saved'));
}, handleErrorResponse);
}
};

View File

@ -200,6 +200,15 @@ class Chart extends React.Component {
return true;
}
}
} else if (
// chart should re-render if color scheme or label color was changed
nextProps.formData?.color_scheme !== this.props.formData?.color_scheme ||
!areObjectsEqual(
nextProps.formData?.label_colors,
this.props.formData?.label_colors,
)
) {
return true;
}
// `cacheBusterProp` is jected by react-hot-loader

View File

@ -66,8 +66,6 @@ interface ChartHolderProps {
updateComponents: Function;
handleComponentDrop: (...args: unknown[]) => unknown;
setFullSizeChartId: (chartId: number | null) => void;
postAddSliceFromDashboard?: () => void;
isInView: boolean;
}
@ -92,7 +90,6 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
updateComponents,
handleComponentDrop,
setFullSizeChartId,
postAddSliceFromDashboard,
isInView,
}) => {
const { chartId } = component.meta;
@ -236,14 +233,6 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
}));
}, []);
const handlePostTransformProps = useCallback(
(props: unknown) => {
postAddSliceFromDashboard?.();
return props;
},
[postAddSliceFromDashboard],
);
return (
<DragDroppable
component={component}
@ -316,7 +305,6 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
isFullSize={isFullSize}
setControlValue={handleExtraControl}
extraControls={extraControls}
postTransformProps={handlePostTransformProps}
isInView={isInView}
/>
{editMode && (

View File

@ -37,7 +37,6 @@ import {
setDirectPathToChild,
setActiveTabs,
setFullSizeChartId,
postAddSliceFromDashboard,
} from 'src/dashboard/actions/dashboardState';
const propTypes = {
@ -112,7 +111,6 @@ function mapDispatchToProps(dispatch) {
setFullSizeChartId,
setActiveTabs,
logEvent,
postAddSliceFromDashboard,
},
dispatch,
);

View File

@ -31,7 +31,8 @@ import {
fetchFaveStar,
saveFaveStar,
savePublished,
setColorSchemeAndUnsavedChanges,
setColorScheme,
setUnsavedChanges,
fetchCharts,
updateCss,
onChange,
@ -112,7 +113,8 @@ function mapDispatchToProps(dispatch) {
onRedo: redoLayoutAction,
setEditMode,
showBuilderPane,
setColorSchemeAndUnsavedChanges,
setColorScheme,
setUnsavedChanges,
fetchFaveStar,
saveFaveStar,
savePublished,

View File

@ -23,6 +23,7 @@ import {
FeatureFlag,
getSharedLabelColor,
isFeatureEnabled,
SharedLabelColorSource,
t,
useTheme,
} from '@superset-ui/core';
@ -336,17 +337,18 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
return () => {};
}, [css]);
useEffect(
() => () => {
useEffect(() => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.source = SharedLabelColorSource.dashboard;
return () => {
// clean up label color
const categoricalNamespace = CategoricalColorNamespace.getNamespace(
metadata?.color_namespace,
);
categoricalNamespace.resetColors();
getSharedLabelColor().clear();
},
[metadata?.color_namespace],
);
sharedLabelColor.clear();
};
}, [metadata?.color_namespace]);
useEffect(() => {
if (datasetsApiError) {

View File

@ -39,7 +39,6 @@ import {
UNSET_FOCUSED_FILTER_FIELD,
SET_ACTIVE_TABS,
SET_FULL_SIZE_CHART_ID,
RESET_SLICE,
ON_FILTERS_REFRESH,
ON_FILTERS_REFRESH_SUCCESS,
SET_DATASETS_STATUS,
@ -60,7 +59,6 @@ export default function dashboardStateReducer(state = {}, action) {
return {
...state,
sliceIds: Array.from(updatedSliceIds),
updateSlice: true,
};
},
[REMOVE_SLICE]() {
@ -73,12 +71,6 @@ export default function dashboardStateReducer(state = {}, action) {
sliceIds: Array.from(updatedSliceIds),
};
},
[RESET_SLICE]() {
return {
...state,
updateSlice: false,
};
},
[TOGGLE_FAVE_STAR]() {
return { ...state, isStarred: action.isStarred };
},
@ -125,7 +117,6 @@ export default function dashboardStateReducer(state = {}, action) {
maxUndoHistoryExceeded: false,
editMode: false,
updatedColorScheme: false,
updateSlice: false,
// server-side returns last_modified_time for latest change
lastModifiedTime: action.lastModifiedTime,
};

View File

@ -28,7 +28,6 @@ import {
TOGGLE_EXPAND_SLICE,
TOGGLE_FAVE_STAR,
UNSET_FOCUSED_FILTER_FIELD,
RESET_SLICE,
} from 'src/dashboard/actions/dashboardState';
import dashboardStateReducer from 'src/dashboard/reducers/dashboardState';
@ -44,7 +43,7 @@ describe('dashboardState reducer', () => {
{ sliceIds: [1] },
{ type: ADD_SLICE, slice: { slice_id: 2 } },
),
).toEqual({ sliceIds: [1, 2], updateSlice: true });
).toEqual({ sliceIds: [1, 2] });
});
it('should remove a slice', () => {
@ -56,12 +55,6 @@ describe('dashboardState reducer', () => {
).toEqual({ sliceIds: [1], filters: {} });
});
it('should reset updateSlice', () => {
expect(
dashboardStateReducer({ updateSlice: true }, { type: RESET_SLICE }),
).toEqual({ updateSlice: false });
});
it('should toggle fav star', () => {
expect(
dashboardStateReducer(

View File

@ -19,7 +19,14 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { isDefined, JsonObject, makeApi, t } from '@superset-ui/core';
import {
getSharedLabelColor,
isDefined,
JsonObject,
makeApi,
SharedLabelColorSource,
t,
} from '@superset-ui/core';
import Loading from 'src/components/Loading';
import { addDangerToast } from 'src/components/MessageToasts/actions';
import { getUrlParam } from 'src/utils/urlUtils';
@ -139,6 +146,7 @@ export default function ExplorePage() {
isExploreInitialized.current = true;
});
}
getSharedLabelColor().source = SharedLabelColorSource.explore;
}, [dispatch, location]);
if (!isLoaded) {

View File

@ -454,7 +454,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = {
"UX_BETA": False,
"GENERIC_CHART_AXES": False,
"ALLOW_ADHOC_SUBQUERY": False,
"USE_ANALAGOUS_COLORS": True,
"USE_ANALAGOUS_COLORS": False,
"DASHBOARD_EDIT_CHART_IN_NEW_TAB": False,
# 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!