Skip to content
Snippets Groups Projects
Unverified Commit 2e9a53a7 authored by Phoomparin Mano's avatar Phoomparin Mano Committed by GitHub
Browse files

feat(sdk): apply user interface color overrides to the sdk (#42834)

* feat(sdk): apply ui color overrides to the sdk

selector state

set global embedding colors

rename the method

* test(sdk): add unit test on color overrides
parent bbaa1803
Branches
Tags
No related merge requests found
import { useMemo } from "react";
import type { MetabaseTheme } from "embedding-sdk";
import {
getEmbeddingThemeOverride,
setGlobalEmbeddingColors,
} from "embedding-sdk/lib/theme";
import { useSelector } from "metabase/lib/redux";
import { getSettings } from "metabase/selectors/settings";
import { ThemeProvider } from "metabase/ui";
import { getApplicationColors } from "metabase-enterprise/settings/selectors";
interface Props {
theme?: MetabaseTheme;
children: React.ReactNode;
}
export const SdkThemeProvider = ({ theme, children }: Props) => {
const appColors = useSelector(state =>
getApplicationColors(getSettings(state)),
);
const themeOverride = useMemo(() => {
// !! Mutate the global colors object to apply the new colors.
// This must be done before ThemeProvider calls getThemeOverrides.
setGlobalEmbeddingColors(theme?.colors, appColors);
return theme && getEmbeddingThemeOverride(theme);
}, [appColors, theme]);
return <ThemeProvider theme={themeOverride}>{children}</ThemeProvider>;
};
import { renderWithProviders, screen } from "__support__/ui";
import { SdkThemeProvider } from "embedding-sdk/components/private/SdkThemeProvider";
import { Text } from "metabase/ui";
import {
createMockSettingsState,
createMockState,
} from "metabase-types/store/mocks";
describe("SdkThemeProvider", () => {
it("should inject colors from appearance settings and sdk themes", () => {
const state = createMockState({
settings: createMockSettingsState({
"application-colors": {
brand: "rgb(11, 11, 11)",
filter: "rgb(33, 33, 33)",
},
}),
});
const theme = {
colors: {
"text-primary": "rgb(22, 22, 22)",
filter: "rgb(44, 44, 44)",
},
};
renderWithProviders(
<SdkThemeProvider theme={theme}>
<div>
<Text c="brand">Brand</Text>
<Text c="text-dark">Text Dark</Text>
<Text c="filter">Filter</Text>
</div>
</SdkThemeProvider>,
{ storeInitialState: state },
);
const brand = window.getComputedStyle(screen.getByText("Brand"));
const primary = window.getComputedStyle(screen.getByText("Text Dark"));
const filter = window.getComputedStyle(screen.getByText("Filter"));
// User interface colors should be applied
expect(brand.color).toBe("rgb(11, 11, 11)");
// SDK colors should be applied if they are not in the user interface colors
expect(primary.color).toBe("rgb(22, 22, 22)");
// SDK colors should override user interface colors
expect(filter.color).toBe("rgb(44, 44, 44)");
});
});
import { type ReactNode, type JSX, useEffect, useMemo } from "react";
import { type ReactNode, type JSX, useEffect } from "react";
import { memo } from "react";
import { Provider } from "react-redux";
import { AppInitializeController } from "embedding-sdk/components/private/AppInitializeController";
import { SdkThemeProvider } from "embedding-sdk/components/private/SdkThemeProvider";
import type { SdkPluginsConfig } from "embedding-sdk/lib/plugins";
import {
getEmbeddingThemeOverride,
getThemedColorsPalette,
} from "embedding-sdk/lib/theme/get-embedding-theme";
import { store } from "embedding-sdk/store";
import {
setErrorComponent,
......@@ -16,11 +13,8 @@ import {
} from "embedding-sdk/store/reducer";
import type { SDKConfig } from "embedding-sdk/types";
import type { MetabaseTheme } from "embedding-sdk/types/theme";
import { colors } from "metabase/lib/colors";
import type { ColorName } from "metabase/lib/colors/types";
import { setOptions } from "metabase/redux/embed";
import { EmotionCacheProvider } from "metabase/styled-components/components/EmotionCacheProvider";
import { ThemeProvider } from "metabase/ui/components/theme/ThemeProvider";
import "metabase/css/vendor.css";
import "metabase/css/index.module.css";
......@@ -38,16 +32,6 @@ const MetabaseProviderInternal = ({
pluginsConfig,
theme,
}: MetabaseProviderProps): JSX.Element => {
const themeOverride = useMemo(() => {
const combinedThemeColors = getThemedColorsPalette(theme?.colors);
Object.entries(combinedThemeColors).forEach(([key, value]) => {
colors[key as ColorName] = value;
});
return theme && getEmbeddingThemeOverride(theme);
}, [theme]);
useEffect(() => {
if (theme?.fontFamily) {
store.dispatch(
......@@ -73,11 +57,11 @@ const MetabaseProviderInternal = ({
return (
<Provider store={store}>
<EmotionCacheProvider>
<ThemeProvider theme={themeOverride}>
<SdkThemeProvider theme={theme}>
<AppInitializeController config={config}>
{children}
</AppInitializeController>
</ThemeProvider>
</SdkThemeProvider>
</EmotionCacheProvider>
</Provider>
);
......
import type { MetabaseColor, MetabaseColors } from "embedding-sdk/types/theme";
import { colors } from "metabase/lib/colors";
import type { ColorName, ColorPalette } from "metabase/lib/colors/types";
export const SDK_TO_MAIN_APP_COLORS_MAPPING: Record<MetabaseColor, ColorName> =
{
brand: "brand",
border: "border",
filter: "filter",
summarize: "summarize",
"text-primary": "text-dark",
"text-secondary": "text-medium",
"text-tertiary": "text-light",
background: "bg-white",
"background-hover": "bg-light",
// shadow: "shadow",
// positive: "success",
// negative: "danger",
// warning: "warning",
// white
// black
};
const originalColors = { ...colors };
/**
* @param sdkColors color overrides from the SDK theme
* @param appPalette color palette from the admin appearance settings
*/
export function getEmbeddingColorPalette(
sdkColors?: MetabaseColors,
appPalette?: ColorPalette,
): ColorPalette {
if (!sdkColors) {
return originalColors;
}
const mappedThemeColors: ColorPalette = {};
Object.entries(sdkColors).forEach(([key, value]) => {
const mappedKey = SDK_TO_MAIN_APP_COLORS_MAPPING[key as MetabaseColor];
mappedThemeColors[mappedKey] = value;
});
return {
...originalColors,
...appPalette,
...mappedThemeColors,
};
}
/**
* !! Mutate the global colors object to apply the new colors.
*
* @param sdkColors color overrides from the SDK theme
* @param appPalette color palette from the admin appearance settings
*/
export function setGlobalEmbeddingColors(
sdkColors?: MetabaseColors,
appPalette?: ColorPalette,
) {
const combinedThemeColors = getEmbeddingColorPalette(sdkColors, appPalette);
Object.entries(combinedThemeColors).forEach(([key, value]) => {
colors[key as ColorName] = value;
});
}
import { merge } from "icepick";
import { DEFAULT_FONT } from "embedding-sdk/config";
import { colors } from "metabase/lib/colors";
import type { ColorName, ColorPalette } from "metabase/lib/colors/types";
import type {
MetabaseTheme,
MetabaseColors,
MetabaseColor,
MetabaseComponentTheme,
} from "../../types/theme";
......@@ -14,6 +11,7 @@ import type { EmbeddingThemeOverride } from "../../types/theme/private";
import { colorTuple } from "./color-tuple";
import { DEFAULT_EMBEDDED_COMPONENT_THEME } from "./default-component-theme";
import { SDK_TO_MAIN_APP_COLORS_MAPPING } from "./embedding-color-palette";
const getFontFamily = (theme: MetabaseTheme) =>
theme.fontFamily ?? DEFAULT_FONT;
......@@ -58,45 +56,3 @@ export function getEmbeddingThemeOverride(
return override;
}
const SDK_TO_MAIN_APP_COLORS_MAPPING: Record<MetabaseColor, ColorName> = {
brand: "brand",
border: "border",
filter: "filter",
summarize: "summarize",
"text-primary": "text-dark",
"text-secondary": "text-medium",
"text-tertiary": "text-light",
background: "bg-white",
"background-hover": "bg-light",
// shadow: "shadow",
// positive: "success",
// negative: "danger",
// warning: "warning",
// white
// black
};
const originalColors = { ...colors };
export function getThemedColorsPalette(
themeColors?: MetabaseColors,
): ColorPalette {
if (!themeColors) {
return originalColors;
}
const mappedThemeColors: ColorPalette = {};
Object.entries(themeColors).forEach(([key, value]) => {
const mappedKey = SDK_TO_MAIN_APP_COLORS_MAPPING[key as MetabaseColor];
mappedThemeColors[mappedKey] = value;
});
return {
...originalColors,
...mappedThemeColors,
};
}
export { DEFAULT_METABASE_COMPONENT_THEME } from "./default-component-theme";
export { getEmbeddingThemeOverride } from "./get-embedding-theme";
export { setGlobalEmbeddingColors } from "./embedding-color-palette";
......@@ -127,3 +127,6 @@ export function getNoObjectIllustration(state: EnterpriseState): string | null {
return getSetting(state, "no-object-illustration-custom") as string;
}
}
export const getApplicationColors = (settingValues: EnterpriseSettings) =>
settingValues["application-colors"];
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment