refactor(extensions): use declare function pattern for colors bridge

Replace the registry-with-state implementation in @apache-superset/core/colors
with declare-only function signatures. The host app (ExtensionsStartup) now
provides the concrete implementations via window.superset.colors, matching
the same pattern used by the authentication module.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-04-16 12:18:19 -07:00
parent d8f2fb7e6e
commit e6218b3efd
3 changed files with 25 additions and 81 deletions

View File

@@ -17,13 +17,7 @@
* under the License.
*/
import {
ColorSchemeGroup,
getCategoricalSchemeNames,
getCategoricalSchemeRegistry,
getSchemeColors,
registerCategoricalSchemeRegistry,
} from '@apache-superset/core/colors';
import { ColorSchemeGroup } from '@apache-superset/core/colors';
// ─── ColorSchemeGroup enum ────────────────────────────────────────────────────
@@ -32,35 +26,3 @@ test('ColorSchemeGroup has the expected string values', () => {
expect(ColorSchemeGroup.Featured).toBe('featured');
expect(ColorSchemeGroup.Other).toBe('other');
});
// ─── Registry bridge ─────────────────────────────────────────────────────────
test('getCategoricalSchemeNames returns [] before any registry is injected', () => {
registerCategoricalSchemeRegistry({ keys: () => [], get: () => null });
expect(getCategoricalSchemeNames()).toEqual([]);
});
test('getCategoricalSchemeNames returns alphabetically sorted names', () => {
registerCategoricalSchemeRegistry({
keys: () => ['zebra', 'apple', 'mango'],
get: () => null,
});
expect(getCategoricalSchemeNames()).toEqual(['apple', 'mango', 'zebra']);
});
test('getSchemeColors returns the color array for a known scheme', () => {
const colors = ['#ff0000', '#00ff00', '#0000ff'];
registerCategoricalSchemeRegistry({
keys: () => ['testScheme'],
get: (name: string) =>
name === 'testScheme' ? { id: 'testScheme', colors } : null,
});
expect(getSchemeColors('testScheme')).toEqual(colors);
expect(getSchemeColors('nonexistent')).toBeNull();
});
test('getCategoricalSchemeRegistry returns the injected registry object', () => {
const mockRegistry = { keys: () => ['a', 'b'], get: () => null };
registerCategoricalSchemeRegistry(mockRegistry);
expect(getCategoricalSchemeRegistry()).toBe(mockRegistry);
});

View File

@@ -21,13 +21,6 @@
export type { ColorSchemeConfig, SequentialSchemeConfig } from './types';
export { ColorSchemeGroup } from './types';
// ─── Registry bridge for extensions ──────────────────────────────────────────
//
// The host app (ExtensionsStartup) calls `registerCategoricalSchemeRegistry()`
// to inject @superset-ui/core's live singleton. Extensions then call
// `getCategoricalSchemeNames()` / `getSchemeColors()` and receive the same
// palettes that the host app registered (including any custom extra schemes).
/**
* Minimal interface for the categorical color scheme registry.
* Mirrors the public surface of @superset-ui/core's ColorSchemeRegistry.
@@ -43,37 +36,16 @@ export interface CategoricalSchemeRegistryLike {
get(name: string): CategoricalScheme | null | undefined;
}
let _registry: CategoricalSchemeRegistryLike | null = null;
/**
* Called by the Superset host app (ExtensionsStartup) to inject the
* categorical color scheme registry so extensions can access it without
* depending directly on @superset-ui/core.
* Returns an alphabetically sorted list of all registered categorical color
* scheme names. The host app (ExtensionsStartup) provides the implementation
* via window.superset.colors.
*/
export function registerCategoricalSchemeRegistry(
registry: CategoricalSchemeRegistryLike,
): void {
_registry = registry;
}
/**
* Returns the categorical color scheme registry registered by the host app,
* or null if not yet injected (e.g. in tests or isolated builds).
*/
export function getCategoricalSchemeRegistry(): CategoricalSchemeRegistryLike | null {
return _registry;
}
/**
* Returns an alphabetically sorted list of all registered scheme names.
*/
export function getCategoricalSchemeNames(): string[] {
return _registry?.keys().sort() ?? [];
}
export declare function getCategoricalSchemeNames(): string[];
/**
* Returns the color array for a named scheme, or null if not found.
* The host app (ExtensionsStartup) provides the implementation
* via window.superset.colors.
*/
export function getSchemeColors(schemeName: string): string[] | null {
return _registry?.get(schemeName)?.colors ?? null;
}
export declare function getSchemeColors(schemeName: string): string[] | null;

View File

@@ -24,6 +24,7 @@ import {
isFeatureEnabled,
getCategoricalSchemeRegistry,
} from '@superset-ui/core';
import type { CategoricalSchemeRegistryLike } from '@apache-superset/core/colors';
import {
authentication,
core,
@@ -49,7 +50,11 @@ declare global {
menus: typeof menus;
sqlLab: typeof sqlLab;
views: typeof views;
colors: typeof supersetCore.colors;
colors: {
ColorSchemeGroup: typeof supersetCore.colors.ColorSchemeGroup;
getCategoricalSchemeNames(): string[];
getSchemeColors(schemeName: string): string[] | null;
};
};
}
}
@@ -73,6 +78,8 @@ const ExtensionsStartup: React.FC<{ children?: React.ReactNode }> = ({
}
// Provide the implementations for @apache-superset/core
const registry =
getCategoricalSchemeRegistry() as CategoricalSchemeRegistryLike | null;
window.superset = {
...supersetCore,
authentication,
@@ -83,14 +90,17 @@ const ExtensionsStartup: React.FC<{ children?: React.ReactNode }> = ({
menus,
sqlLab,
views,
colors: {
ColorSchemeGroup: supersetCore.colors.ColorSchemeGroup,
getCategoricalSchemeNames(): string[] {
return (registry?.keys() ?? []).sort();
},
getSchemeColors(schemeName: string): string[] | null {
return registry?.get(schemeName)?.colors ?? null;
},
},
};
// Inject the categorical color scheme registry so extensions can access
// Superset's registered palettes without depending on @superset-ui/core.
supersetCore.colors.registerCategoricalSchemeRegistry(
getCategoricalSchemeRegistry(),
);
const setup = async () => {
if (isFeatureEnabled(FeatureFlag.EnableExtensions)) {
try {