You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/09/28 17:37:16 UTC

(superset) branch master updated: test(dashboard): cover color scheme precedence and label color map helpers (#44677)

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 3667f5bcaa6 test(dashboard): cover color scheme precedence and label color map helpers (#44677)
3667f5bcaa6 is described below

commit 3667f5bcaa6aa2364f80695620099b93696da83d
Author: Joe Li <jo...@preset.io>
AuthorDate: Mon Sep 28 10:37:08 2026 -0700

    test(dashboard): cover color scheme precedence and label color map helpers (#44677)
    
    Co-authored-by: Claude Opus 5.5 <no...@anthropic.com>
---
 superset-frontend/src/utils/colorScheme.test.ts | 230 ++++++++++++++++++++++++
 1 file changed, 230 insertions(+)

diff --git a/superset-frontend/src/utils/colorScheme.test.ts b/superset-frontend/src/utils/colorScheme.test.ts
new file mode 100644
index 00000000000..92dfc9ddf75
--- /dev/null
+++ b/superset-frontend/src/utils/colorScheme.test.ts
@@ -0,0 +1,230 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {
+  CategoricalColorNamespace,
+  CategoricalScheme,
+  getCategoricalSchemeRegistry,
+  getLabelsColorMap,
+} from '@superset-ui/core';
+import {
+  applyColors,
+  enforceSharedLabelsColorsArray,
+  getColorNamespace,
+  getColorSchemeDomain,
+  getDynamicLabelsColors,
+  getFreshLabelsColorMapEntries,
+  getFreshSharedLabels,
+  getSharedLabelsColorMapEntries,
+  isLabelsColorMapSynced,
+  refreshLabelsColorMap,
+  resetColors,
+} from './colorScheme';
+
+const registry = getCategoricalSchemeRegistry();
+
+beforeEach(() => {
+  registry.clear();
+  registry.registerValue(
+    'SUPERSET_DEFAULT',
+    new CategoricalScheme({
+      id: 'SUPERSET_DEFAULT',
+      colors: ['#111111', '#222222', '#333333'],
+    }),
+  );
+  getLabelsColorMap().reset();
+  CategoricalColorNamespace.getNamespace().resetColors();
+});
+
+test('getColorNamespace falls back to the global namespace for falsy values', () => {
+  expect(getColorNamespace('')).toBeUndefined();
+  expect(getColorNamespace(undefined)).toBeUndefined();
+  expect(getColorNamespace('custom-namespace')).toBe('custom-namespace');
+});
+
+test('enforceSharedLabelsColorsArray returns an empty array for legacy non-array values', () => {
+  expect(enforceSharedLabelsColorsArray(['Alpha', 'Beta'])).toEqual([
+    'Alpha',
+    'Beta',
+  ]);
+  // the field used to be a dict keyed by label; that shape must not leak through
+  expect(enforceSharedLabelsColorsArray({ Alpha: '#111111' })).toEqual([]);
+  expect(enforceSharedLabelsColorsArray(undefined)).toEqual([]);
+});
+
+test('getFreshSharedLabels merges stored shared labels with labels reused across charts', () => {
+  const labelsColorMap = getLabelsColorMap();
+  labelsColorMap.addSlice('Alpha', '#111111', 1);
+  labelsColorMap.addSlice('Alpha', '#111111', 2); // reused by a second chart
+  labelsColorMap.addSlice('Beta', '#222222', 1); // used by only one chart
+
+  const freshShared = getFreshSharedLabels(['Gamma']);
+
+  expect(freshShared.sort()).toEqual(['Alpha', 'Gamma'].sort());
+});
+
+test('getSharedLabelsColorMapEntries keeps only the labels marked as shared', () => {
+  const fullLabelsColor = { Alpha: '#111111', Beta: '#222222' };
+
+  expect(getSharedLabelsColorMapEntries(fullLabelsColor, ['Alpha'])).toEqual({
+    Alpha: '#111111',
+  });
+  expect(getSharedLabelsColorMapEntries(fullLabelsColor, [])).toEqual({});
+});
+
+test('getFreshLabelsColorMapEntries excludes labels overridden by custom label colors', () => {
+  getLabelsColorMap().addSlice('Alpha', '#custom', 1);
+  getLabelsColorMap().addSlice('Beta', '#222222', 1);
+
+  const entries = getFreshLabelsColorMapEntries({ Alpha: '#custom' });
+
+  expect(entries).toEqual({ Beta: '#222222' });
+});
+
+test('getDynamicLabelsColors omits the custom label color keys from the full map', () => {
+  const fullLabelsColors = { Alpha: '#111111', Beta: '#222222' };
+
+  expect(
+    getDynamicLabelsColors(fullLabelsColors, { Alpha: '#custom' }),
+  ).toEqual({ Beta: '#222222' });
+});
+
+test('getColorSchemeDomain returns the registered scheme, falls back to the default scheme, or [] when none is registered', () => {
+  registry.registerValue(
+    'scheme-a',
+    new CategoricalScheme({ id: 'scheme-a', colors: ['#a1', '#a2'] }),
+  );
+
+  expect(getColorSchemeDomain('scheme-a')).toEqual(['#a1', '#a2']);
+
+  // an unknown scheme (e.g. one removed after the dashboard was saved) falls
+  // back to the default scheme
+  expect(getColorSchemeDomain('does-not-exist')).toEqual([
+    '#111111',
+    '#222222',
+    '#333333',
+  ]);
+
+  // no scheme registered at all: there is nothing to fall back to
+  registry.clear();
+  expect(getColorSchemeDomain('does-not-exist')).toEqual([]);
+});
+
+test('isLabelsColorMapSynced ignores drift on custom labels but detects it elsewhere', () => {
+  const stored = { Alpha: '#111111', Beta: '#222222', Custom: '#999999' };
+  const freshInSync = { Alpha: '#111111', Beta: '#222222', Custom: '#000000' };
+  const freshDrifted = {
+    Alpha: '#111111',
+    Beta: '#changed',
+    Custom: '#000000',
+  };
+
+  expect(isLabelsColorMapSynced(stored, freshInSync, { Custom: '' })).toBe(
+    true,
+  );
+  expect(isLabelsColorMapSynced(stored, freshDrifted, { Custom: '' })).toBe(
+    false,
+  );
+});
+
+test('resetColors wipes both the forced namespace colors and the labels color map', () => {
+  const namespace = 'ns-reset';
+  CategoricalColorNamespace.getNamespace(namespace).setColor(
+    'Alpha',
+    '#custom',
+  );
+  getLabelsColorMap().addSlice('Alpha', '#abcabc', 1);
+
+  resetColors(namespace);
+
+  expect(CategoricalColorNamespace.getNamespace(namespace).forcedItems).toEqual(
+    {},
+  );
+  expect(getLabelsColorMap().getColorMap().size).toBe(0);
+  expect(getLabelsColorMap().chartsLabelsMap.size).toBe(0);
+});
+
+test('refreshLabelsColorMap lets a dashboard-level scheme override a chart own scheme', () => {
+  const namespace = 'ns-scheme-precedence';
+  registry.registerValue(
+    'chart-scheme',
+    new CategoricalScheme({ id: 'chart-scheme', colors: ['#chartcolor'] }),
+  );
+  registry.registerValue(
+    'dashboard-scheme',
+    new CategoricalScheme({ id: 'dashboard-scheme', colors: ['#dashcolor'] }),
+  );
+  const labelsColorMap = getLabelsColorMap();
+  labelsColorMap.addSlice('Alpha', '#ignored', 1, 'chart-scheme');
+  labelsColorMap.setOwnColorScheme(1, 'chart-scheme');
+
+  // a dashboard-level scheme wins over the chart's own scheme
+  refreshLabelsColorMap(namespace, 'dashboard-scheme');
+  expect(labelsColorMap.getColorMap().get('Alpha')).toBe('#dashcolor');
+
+  // once no dashboard scheme is set, the chart falls back to its own scheme
+  refreshLabelsColorMap(namespace, undefined);
+  expect(labelsColorMap.getColorMap().get('Alpha')).toBe('#chartcolor');
+});
+
+test('applyColors always lets custom label_colors win over shared label colors', () => {
+  const namespace = 'ns-custom-over-shared';
+  const metadata = {
+    color_namespace: namespace,
+    map_label_colors: { Alpha: '#111111', Beta: '#222222' },
+    shared_label_colors: ['Alpha', 'Beta'],
+    label_colors: { Alpha: '#custom' },
+  };
+
+  applyColors(metadata, false, false, true);
+
+  const { forcedItems } = CategoricalColorNamespace.getNamespace(namespace);
+  expect(forcedItems.Alpha).toBe('#custom');
+  expect(forcedItems.Beta).toBe('#222222');
+});
+
+test('applyColors with the shared flag only applies colors for shared labels', () => {
+  const namespace = 'ns-shared-only';
+  const metadata = {
+    color_namespace: namespace,
+    map_label_colors: { Alpha: '#111111', Gamma: '#333333' },
+    shared_label_colors: ['Alpha'],
+  };
+
+  applyColors(metadata, false, false, true);
+
+  const { forcedItems } = CategoricalColorNamespace.getNamespace(namespace);
+  expect(forcedItems.Alpha).toBe('#111111');
+  expect(forcedItems.Gamma).toBeUndefined();
+});
+
+test('applyColors with a fresh label list only resets the targeted labels', () => {
+  const namespace = 'ns-fresh-array';
+  const categoricalNamespace =
+    CategoricalColorNamespace.getNamespace(namespace);
+  categoricalNamespace.setColor('A', '#oldA');
+  categoricalNamespace.setColor('B', '#oldB');
+
+  const metadata = { color_namespace: namespace };
+  applyColors(metadata, ['A'], false, false);
+
+  const { forcedItems } = categoricalNamespace;
+  expect(forcedItems.A).toBeUndefined();
+  expect(forcedItems.B).toBe('#oldB');
+});