mirror of
https://github.com/apache/superset.git
synced 2026-09-01 13:01:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad7ea1fb11 | ||
|
|
2c42568e78 | ||
|
|
b1362738ce |
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 { ColorSchemeGroup } from '@apache-superset/core/theme';
|
||||
|
||||
test('ColorSchemeGroup has the expected string values', () => {
|
||||
expect(ColorSchemeGroup.Custom).toBe('custom');
|
||||
expect(ColorSchemeGroup.Featured).toBe('featured');
|
||||
expect(ColorSchemeGroup.Other).toBe('other');
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Grouping/tier for a color scheme — controls how it appears in the
|
||||
* scheme picker UI (e.g. Featured palettes are shown first).
|
||||
*
|
||||
* Mirrors @superset-ui/core's ColorSchemeGroup; kept here so
|
||||
* palette configs have no dependency on @superset-ui/core.
|
||||
*/
|
||||
export enum ColorSchemeGroup {
|
||||
Custom = 'custom',
|
||||
Featured = 'featured',
|
||||
Other = 'other',
|
||||
}
|
||||
|
||||
/** Plain configuration object for a categorical color scheme. */
|
||||
export interface CategoricalScheme {
|
||||
id: string;
|
||||
label?: string;
|
||||
colors: string[];
|
||||
description?: string;
|
||||
isDefault?: boolean;
|
||||
group?: ColorSchemeGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequential / diverging color scheme. The only difference from a
|
||||
* categorical scheme is the optional `isDiverging` flag.
|
||||
*/
|
||||
export interface SequentialScheme extends CategoricalScheme {
|
||||
isDiverging?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal interface for the categorical color scheme registry.
|
||||
* Mirrors the public surface of @superset-ui/core's ColorSchemeRegistry.
|
||||
*/
|
||||
export interface CategoricalSchemeRegistryLike {
|
||||
keys(): string[];
|
||||
get(name: string): CategoricalScheme | null | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an alphabetically sorted list of all registered categorical color
|
||||
* scheme names. The host app provides the implementation via
|
||||
* window.superset.theme.
|
||||
*/
|
||||
export declare function getCategoricalSchemeNames(): string[];
|
||||
|
||||
/**
|
||||
* Returns the full list of registered categorical color schemes (id, colors,
|
||||
* and any other available metadata), sorted alphabetically by id. Prefer
|
||||
* this over `getCategoricalSchemeNames` when scheme metadata (label,
|
||||
* description, etc.) is needed, since extracting just the names would
|
||||
* require a second round-trip through `getSchemeColors` per scheme.
|
||||
* The host app provides the implementation via window.superset.theme.
|
||||
*/
|
||||
export declare function getCategoricalSchemes(): CategoricalScheme[];
|
||||
|
||||
/**
|
||||
* Returns the color array for a named scheme, or null if not found.
|
||||
* The host app provides the implementation via window.superset.theme.
|
||||
*/
|
||||
export declare function getSchemeColors(schemeName: string): string[] | null;
|
||||
@@ -88,3 +88,7 @@ export type {
|
||||
// Export theme utility functions
|
||||
export * from './utils/themeUtils';
|
||||
export * from './utils';
|
||||
|
||||
// Color scheme API — types, enum, and declare-function bridge for extensions.
|
||||
// The host app provides the runtime implementations on window.superset.theme.
|
||||
export * from './colors';
|
||||
|
||||
@@ -24,6 +24,12 @@ import {
|
||||
CategoricalD3,
|
||||
CategoricalGoogle,
|
||||
CategoricalLyft,
|
||||
CategoricalModernSunset,
|
||||
CategoricalColorsOfRainbow,
|
||||
CategoricalBlueToGreen,
|
||||
CategoricalRedToYellow,
|
||||
CategoricalWavesOfBlue,
|
||||
CategoricalPresetSuperset,
|
||||
SequentialCommon,
|
||||
SequentialD3,
|
||||
CategoricalScheme,
|
||||
@@ -41,22 +47,59 @@ describe('Color Schemes', () => {
|
||||
CategoricalLyft,
|
||||
CategoricalSuperset,
|
||||
CategoricalPreset,
|
||||
CategoricalModernSunset,
|
||||
CategoricalColorsOfRainbow,
|
||||
CategoricalBlueToGreen,
|
||||
CategoricalRedToYellow,
|
||||
CategoricalWavesOfBlue,
|
||||
CategoricalPresetSuperset,
|
||||
].forEach(group => {
|
||||
expect(group).toBeInstanceOf(Array);
|
||||
expect(group.length).toBeGreaterThan(0);
|
||||
group.forEach(scheme =>
|
||||
expect(scheme).toBeInstanceOf(CategoricalScheme),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('each scheme has a non-empty id and at least one color', () => {
|
||||
[
|
||||
...CategoricalAirbnb,
|
||||
...CategoricalD3,
|
||||
...CategoricalEcharts,
|
||||
...CategoricalGoogle,
|
||||
...CategoricalLyft,
|
||||
...CategoricalPreset,
|
||||
...CategoricalSuperset,
|
||||
...CategoricalPresetSuperset,
|
||||
...CategoricalModernSunset,
|
||||
...CategoricalColorsOfRainbow,
|
||||
...CategoricalBlueToGreen,
|
||||
...CategoricalRedToYellow,
|
||||
...CategoricalWavesOfBlue,
|
||||
].forEach(scheme => {
|
||||
expect(scheme.id).toBeTruthy();
|
||||
expect(scheme.colors.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sequential', () => {
|
||||
test('returns an array of SequentialScheme', () => {
|
||||
[SequentialCommon, SequentialD3].forEach(group => {
|
||||
expect(group).toBeInstanceOf(Array);
|
||||
expect(group.length).toBeGreaterThan(0);
|
||||
group.forEach(scheme =>
|
||||
expect(scheme).toBeInstanceOf(SequentialScheme),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('each scheme has a non-empty id and at least two colors', () => {
|
||||
[...SequentialCommon, ...SequentialD3].forEach(scheme => {
|
||||
expect(scheme.id).toBeTruthy();
|
||||
expect(scheme.colors.length).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,5 +36,6 @@ export * from './models';
|
||||
export * from './navigation';
|
||||
export * from './sqlLab';
|
||||
export * from './storage';
|
||||
export * from './theme';
|
||||
export * from './utils';
|
||||
export * from './views';
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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 { getCategoricalSchemeRegistry } from '@superset-ui/core';
|
||||
import { theme } from './colors';
|
||||
|
||||
jest.mock('@superset-ui/core', () => ({
|
||||
getCategoricalSchemeRegistry: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockGetRegistry = getCategoricalSchemeRegistry as jest.Mock;
|
||||
|
||||
const supersetScheme = { id: 'supersetColors', colors: ['#111', '#222'] };
|
||||
const airbnbScheme = { id: 'airbnb', colors: ['#333', '#444'] };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('getCategoricalSchemeNames returns an empty array when there is no registry', () => {
|
||||
mockGetRegistry.mockReturnValue(null);
|
||||
expect(theme.getCategoricalSchemeNames()).toEqual([]);
|
||||
});
|
||||
|
||||
test('getCategoricalSchemeNames returns registered names sorted alphabetically', () => {
|
||||
mockGetRegistry.mockReturnValue({
|
||||
keys: () => ['supersetColors', 'airbnb'],
|
||||
get: jest.fn(),
|
||||
});
|
||||
expect(theme.getCategoricalSchemeNames()).toEqual([
|
||||
'airbnb',
|
||||
'supersetColors',
|
||||
]);
|
||||
});
|
||||
|
||||
test('getSchemeColors returns null when there is no registry', () => {
|
||||
mockGetRegistry.mockReturnValue(null);
|
||||
expect(theme.getSchemeColors('airbnb')).toBeNull();
|
||||
});
|
||||
|
||||
test('getSchemeColors returns null when the scheme is not found', () => {
|
||||
mockGetRegistry.mockReturnValue({ keys: () => [], get: () => undefined });
|
||||
expect(theme.getSchemeColors('unknown')).toBeNull();
|
||||
});
|
||||
|
||||
test('getSchemeColors returns the colors for a registered scheme', () => {
|
||||
mockGetRegistry.mockReturnValue({
|
||||
keys: () => ['airbnb'],
|
||||
get: (name: string) => (name === 'airbnb' ? airbnbScheme : undefined),
|
||||
});
|
||||
expect(theme.getSchemeColors('airbnb')).toEqual(airbnbScheme.colors);
|
||||
});
|
||||
|
||||
test('getCategoricalSchemes returns full scheme metadata sorted by name', () => {
|
||||
mockGetRegistry.mockReturnValue({
|
||||
keys: () => ['supersetColors', 'airbnb'],
|
||||
get: (name: string) => {
|
||||
if (name === 'supersetColors') return supersetScheme;
|
||||
if (name === 'airbnb') return airbnbScheme;
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
expect(theme.getCategoricalSchemes()).toEqual([airbnbScheme, supersetScheme]);
|
||||
});
|
||||
|
||||
test('getCategoricalSchemes filters out names the registry cannot resolve', () => {
|
||||
mockGetRegistry.mockReturnValue({
|
||||
keys: () => ['airbnb', 'missing'],
|
||||
get: (name: string) => (name === 'airbnb' ? airbnbScheme : undefined),
|
||||
});
|
||||
expect(theme.getCategoricalSchemes()).toEqual([airbnbScheme]);
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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 { theme as themeApi } from '@apache-superset/core';
|
||||
import { getCategoricalSchemeRegistry } from '@superset-ui/core';
|
||||
import type {
|
||||
CategoricalScheme,
|
||||
CategoricalSchemeRegistryLike,
|
||||
} from '@apache-superset/core/theme';
|
||||
|
||||
const getRegistry = () =>
|
||||
getCategoricalSchemeRegistry() as CategoricalSchemeRegistryLike | null;
|
||||
|
||||
const getCategoricalSchemeNames: typeof themeApi.getCategoricalSchemeNames =
|
||||
() => (getRegistry()?.keys() ?? []).sort();
|
||||
|
||||
const getCategoricalSchemes: typeof themeApi.getCategoricalSchemes = () => {
|
||||
const registry = getRegistry();
|
||||
return getCategoricalSchemeNames()
|
||||
.map(name => registry?.get(name))
|
||||
.filter((scheme): scheme is CategoricalScheme => scheme != null);
|
||||
};
|
||||
|
||||
const getSchemeColors: typeof themeApi.getSchemeColors = schemeName =>
|
||||
getRegistry()?.get(schemeName)?.colors ?? null;
|
||||
|
||||
/**
|
||||
* Host implementation of the @apache-superset/core/theme color API.
|
||||
* Spreads the contract namespace (types, enum, styling helpers) and supplies
|
||||
* the runtime implementations for the declare-only registry bridge functions.
|
||||
*/
|
||||
export const theme: typeof themeApi = {
|
||||
...themeApi,
|
||||
getCategoricalSchemeNames,
|
||||
getCategoricalSchemes,
|
||||
getSchemeColors,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export * from './colors';
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
navigation,
|
||||
useNavigationTracker,
|
||||
sqlLab,
|
||||
theme,
|
||||
views,
|
||||
} from 'src/core';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -64,6 +65,7 @@ const ExtensionsStartup: React.FC<{ children?: React.ReactNode }> = ({
|
||||
menus,
|
||||
navigation,
|
||||
sqlLab,
|
||||
theme,
|
||||
views,
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
menus,
|
||||
navigation,
|
||||
sqlLab,
|
||||
theme,
|
||||
views,
|
||||
} from 'src/core';
|
||||
|
||||
@@ -50,6 +51,7 @@ export interface Namespaces {
|
||||
menus: typeof menus;
|
||||
navigation: typeof navigation;
|
||||
sqlLab: typeof sqlLab;
|
||||
theme: typeof theme;
|
||||
views: typeof views;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user