feat(ui/theme): Support styled-components theming; clean up ant theming (#14787)

This commit is contained in:
Andrew Sikowitz 2025-10-28 15:36:55 -07:00 committed by GitHub
parent bc814fd328
commit 653e5a5b7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 832 additions and 209 deletions

View File

@ -1,3 +1,3 @@
REACT_APP_THEME_CONFIG=theme_light.config.json
ANT_THEME_CONFIG=ant_theme_v2.json
SKIP_PREFLIGHT_CHECK=true
REACT_APP_PROXY_TARGET=http://localhost:9002

View File

@ -82,16 +82,20 @@ you can change two things without rebuilding.
#### Selecting a theme
Theme configurations are stored in `./src/conf/theme`. To select a theme, choose one and update the `REACT_APP_THEME_CONFIG` env variable stored in `.env`.
To change the selected theme, update the `.env` file and re-run `yarn start` from `datahub/datahub-web-react`.
Theme configurations are defined in `./src/conf/theme/themes.ts`. By default, the theme is chosen based on the `REACT_APP_CUSTOM_THEME_ID` env variable in GMS. If no theme is specified, the default themes `themeV2` or `themeV1` are used based on whether the V2 UI is enabled, which is controlled by environment variables `THEME_V2_ENABLED`, `THEME_V2_DEFAULT`, and `THEME_V2_TOGGLEABLE` in GMS. See `metadata-service/configuration/src/main/resources/application.yaml` for more details.
For quick local development, you can set env variable `REACT_APP_THEME` in `.env` to any of the themes defined in `themes.ts`.
We are transitioning away from Ant theming, but still depend on it for some styling. The Ant theme is stored in json files, in `./src/conf/theme`. To select the Ant theme, choose a json file and set env variable `ANT_THEME_CONFIG` in `.env` to the theme's filename, including `.json`, then re-run `yarn start` from `datahub/datahub-web-react`.
#### Editing a theme
To edit an existing theme, the recommendation is to clone one of the existing themes into a new file with the name `<your_themes_name>.config.json`,
and then update the env variable as descibed above. The theme files have three sections, `styles`, `assets` and `content`. The type of the theme configs is specified
in `./src/conf/theme/types.ts`.
To edit an existing theme, the recommendation is to clone one of the existing themes into a new file with the name `<your_themes_name>.ts`, then update `themes.ts` by importing your theme and adding it to the `themes` object. You can also create a json theme by creating a new file in `./src/conf/theme` with the name `<your_themes_name>.config.json`. The theme interface is defined in `./src/conf/theme/types.ts` and has four sections:
`styles` configure overrides for the apps theming variables.
`colors` configures semantic color tokens.
These are not yet widely used but will be the primary way to configure colors in the app going forward.
`styles` configures overrides for the app's deprecated theming variables and for Ant components.
`assets` configures the logo url.

View File

@ -1,41 +1,29 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { ThemeProvider } from 'styled-components';
import { loadThemeIdFromLocalStorage } from '@app/useSetAppTheme';
import defaultThemeConfig from '@conf/theme/theme_light.config.json';
import { useIsThemeV2 } from '@app/useIsThemeV2';
import { useCustomThemeId } from '@app/useSetAppTheme';
import themes from '@conf/theme/themes';
import { Theme } from '@conf/theme/types';
import { CustomThemeContext } from '@src/customThemeContext';
interface Props {
children: React.ReactNode;
skipSetTheme?: boolean;
}
const CustomThemeProvider = ({ children, skipSetTheme }: Props) => {
const [currentTheme, setTheme] = useState<Theme>(defaultThemeConfig);
const customThemeId = loadThemeIdFromLocalStorage();
const CustomThemeProvider = ({ children }: Props) => {
// Note: AppConfigContext not provided yet, so both of these calls rely on the DEFAULT_APP_CONFIG
const isThemeV2 = useIsThemeV2();
const customThemeId = useCustomThemeId();
useEffect(() => {
// use provided customThemeId and set in useSetAppTheme.tsx if it exists
if (customThemeId) return;
if (import.meta.env.DEV) {
import(/* @vite-ignore */ `./conf/theme/${import.meta.env.REACT_APP_THEME_CONFIG}`).then((theme) => {
setTheme(theme);
});
} else if (!skipSetTheme) {
// Send a request to the server to get the theme config.
fetch(`assets/conf/theme/${import.meta.env.REACT_APP_THEME_CONFIG}`)
.then((response) => response.json())
.then((theme) => {
setTheme(theme);
});
}
}, [skipSetTheme, customThemeId]);
// Note: If custom theme id is a json file, it will only be loaded later in useSetAppTheme
const defaultTheme = isThemeV2 ? themes.themeV2 : themes.themeV1;
const customTheme = customThemeId ? themes[customThemeId] : null;
const [theme, setTheme] = useState<Theme>(customTheme ?? defaultTheme);
return (
<CustomThemeContext.Provider value={{ theme: currentTheme, updateTheme: setTheme }}>
<ThemeProvider theme={currentTheme}>{children}</ThemeProvider>
<CustomThemeContext.Provider value={{ theme, updateTheme: setTheme }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</CustomThemeContext.Provider>
);
};

View File

@ -5,12 +5,12 @@ import { ThemeProvider } from 'styled-components';
import { DefineRecipeStep } from '@app/ingest/source/builder/DefineRecipeStep';
import { SourceConfig } from '@app/ingest/source/builder/types';
import defaultThemeConfig from '@conf/theme/theme_light.config.json';
import themeV1 from '@conf/theme/themeV1';
describe('DefineRecipeStep', () => {
it('should render the RecipeBuilder if the type is in CONNECTORS_WITH_FORM', () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={defaultThemeConfig}>
<ThemeProvider theme={themeV1}>
<MockedProvider>
<DefineRecipeStep
state={{ type: 'snowflake' }}
@ -31,7 +31,7 @@ describe('DefineRecipeStep', () => {
it('should not render the RecipeBuilder if the type is not in CONNECTORS_WITH_FORM', () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={defaultThemeConfig}>
<ThemeProvider theme={themeV1}>
<MockedProvider>
<DefineRecipeStep
state={{ type: 'glue' }}

View File

@ -567,7 +567,7 @@ describe('formatTimezone', () => {
expect(['EST', 'EDT']).toContain(nycAbbr);
const londonAbbr = formatTimezone('Europe/London');
expect(['GMT+1', 'BST']).toContain(londonAbbr);
expect(['GMT+1', 'BST', 'GMT']).toContain(londonAbbr);
// Tokyo doesn't observe DST, so it's always GMT+9
expect(formatTimezone('Asia/Tokyo')).toBe('GMT+9');

View File

@ -5,12 +5,12 @@ import { ThemeProvider } from 'styled-components';
import { DefineRecipeStep } from '@app/ingestV2/source/builder/DefineRecipeStep';
import { SourceConfig } from '@app/ingestV2/source/builder/types';
import defaultThemeConfig from '@conf/theme/theme_light.config.json';
import themeV2 from '@conf/theme/themeV2';
describe('DefineRecipeStep', () => {
it('should render the RecipeBuilder if the type is in CONNECTORS_WITH_FORM', () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={defaultThemeConfig}>
<ThemeProvider theme={themeV2}>
<MockedProvider>
<DefineRecipeStep
state={{ type: 'snowflake' }}
@ -31,7 +31,7 @@ describe('DefineRecipeStep', () => {
it('should not render the RecipeBuilder if the type is not in CONNECTORS_WITH_FORM', () => {
const { getByText, queryByText } = render(
<ThemeProvider theme={defaultThemeConfig}>
<ThemeProvider theme={themeV2}>
<MockedProvider>
<DefineRecipeStep
state={{ type: 'glue' }}

View File

@ -2,15 +2,16 @@ import { useEffect } from 'react';
import { useAppConfig } from '@app/useAppConfig';
import { useIsThemeV2 } from '@app/useIsThemeV2';
import themes from '@conf/theme/themes';
import { useCustomTheme } from '@src/customThemeContext';
// add new theme ids here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
enum ThemeId {}
function useCustomThemeId() {
export function useCustomThemeId(): string | null {
const { config, loaded } = useAppConfig();
if (import.meta.env.REACT_APP_THEME) {
return import.meta.env.REACT_APP_THEME;
}
if (!loaded) {
return loadThemeIdFromLocalStorage();
}
@ -20,7 +21,6 @@ function useCustomThemeId() {
export function useSetAppTheme() {
const isThemeV2 = useIsThemeV2();
const { config } = useAppConfig();
const { updateTheme } = useCustomTheme();
const customThemeId = useCustomThemeId();
@ -29,14 +29,24 @@ export function useSetAppTheme() {
}, [customThemeId]);
useEffect(() => {
// here is where we can start adding new custom themes based on customThemeId
if (isThemeV2) {
import('../conf/theme/theme_v2.config.json').then((theme) => updateTheme(theme));
if (customThemeId && customThemeId.endsWith('.json')) {
if (import.meta.env.DEV) {
import(/* @vite-ignore */ `./conf/theme/${customThemeId}`).then((theme) => {
updateTheme(theme);
});
} else {
fetch(`assets/conf/theme/${customThemeId}`)
.then((response) => response.json())
.then((theme) => {
updateTheme(theme);
});
}
} else if (customThemeId && themes[customThemeId]) {
updateTheme(themes[customThemeId]);
} else {
import('../conf/theme/theme_light.config.json').then((theme) => updateTheme(theme));
updateTheme(isThemeV2 ? themes.themeV2 : themes.themeV1);
}
}, [config, isThemeV2, updateTheme, customThemeId]);
}, [customThemeId, isThemeV2, updateTheme]);
}
function setThemeIdLocalStorage(customThemeId: string | null) {
@ -49,7 +59,7 @@ function setThemeIdLocalStorage(customThemeId: string | null) {
const CUSTOM_THEME_ID_KEY = 'customThemeId';
export function loadThemeIdFromLocalStorage(): string | null {
function loadThemeIdFromLocalStorage(): string | null {
return localStorage.getItem(CUSTOM_THEME_ID_KEY);
}

View File

@ -0,0 +1,17 @@
{
"primary-color": "#1890ff",
"layout-header-background": "white",
"layout-header-color": "#434343",
"layout-body-background": "white",
"component-background": "white",
"body-background": "white",
"border-color-base": "#ececec",
"text-color": "fade(black, 85%)",
"text-color-secondary": "fade(black, 45%)",
"heading-color": "fade(black, 85%)",
"background-color-light": "hsv(0, 0, 98%)",
"divider-color": "fade(black, 6%)",
"disabled-color": "fade(black, 25%)",
"steps-nav-arrow-color": "fade(black, 25%)",
"highlight-color": "#E6F4FF"
}

View File

@ -0,0 +1,18 @@
{
"primary-color": "#533FD1",
"link-color": "#533FD1",
"layout-header-background": "white",
"layout-header-color": "#434343",
"layout-body-background": "white",
"component-background": "white",
"body-background": "white",
"border-color-base": "#ececec",
"text-color": "fade(black, 85%)",
"text-color-secondary": "fade(black, 45%)",
"heading-color": "fade(black, 85%)",
"background-color-light": "hsv(0, 0, 98%)",
"divider-color": "fade(black, 6%)",
"disabled-color": "fade(black, 25%)",
"steps-nav-arrow-color": "fade(black, 25%)",
"highlight-color": "#ece9f8"
}

View File

@ -0,0 +1,132 @@
// Should not be imported directly
export default {
gray0: '#FFFFFF',
gray100: '#EBECF0',
gray200: '#F5F6FA',
gray300: '#E9EAEE',
gray400: '#F9FAFC',
gray500: '#A3A7B9',
gray600: '#8088A3',
gray700: '#5F6685',
gray800: '#374066',
gray900: '#323A5D',
gray1000: '#272D48',
gray1100: '#1E2338',
gray1200: '#171B2B',
green0: '#F1F8EE',
green100: '#E1F0D6',
green150: '#ABD58B',
green200: '#248F5B',
green300: '#0D7543',
mudgreen0: '#F7FBF4',
mudgreen100: '#D5E9C9',
mudgreen200: '#C0DEAF',
mudgreen300: '#A4CF8A',
mudgreen400: '#92C573',
mudgreen500: '#77B750',
mudgreen600: '#6CA749',
mudgreen700: '#548239',
mudgreen800: '#41652C',
mudgreen900: '#324D22',
darkgreen0: '#E8F5E9',
darkgreen100: '#C7E5C8',
darkgreen200: '#81C684',
darkgreen300: '#1F7523',
brown0: '#EDEDED',
brown100: '#BCAAA4',
brown200: '#BCAAA4',
brown300: '#5D4037',
trueyellow0: '#FFF8E1',
trueyellow100: '#FFE082',
trueyellow200: '#FBC02D',
trueyellow300: '#9F642F',
tangerine0: '#FFF3E0',
tangerine100: '#FFD8B1',
tangerine200: '#FFD8B1',
tangerine300: '#BF4636',
orange0: '#FFF5F0',
orange100: '#FFE6DA',
orange200: '#FFC0A4',
orange300: '#FF9B6D',
orange400: '#FF8348',
orange500: '#FF6A24',
orange600: '#FF5D13',
orange700: '#FF4F01',
red0: '#FBF3EF',
red50: '#EEB4B4',
red100: '#E54D1F',
red200: '#D23939',
red300: '#C4360B',
wine0: '#F6D5D5',
wine100: '#F3DACE',
wine200: '#F2C1C1',
wine300: '#ECA5A5',
wine400: '#E99393',
wine500: '#E37878',
wine600: '#CF6D6D',
wine700: '#A15555',
wine800: '#7D4242',
wine900: '#5F3232',
yellow0: '#FFFAEB',
yellow100: '#FFF1C7',
yellow200: '#FCEDC7',
yellow300: '#FAE4AB',
yellow400: '#F8D785',
yellow500: '#F6D06D',
yellow600: '#EEAE09',
yellow700: '#EE9521',
yellow800: '#C77100',
violet0: '#F1F3FD',
cyan00: '#E0F2F1',
cyan100: '#80DEEA',
cyan200: '#29C7DC',
cyan300: '#10737F',
cobalt0: '#E8EAF6',
trueblue0: '#E3F2FD',
trueblue100: '#90CAF9',
trueblue200: '#90CAF9',
trueblue300: '#0764C0',
cobalt100: '#C5CAE9',
cobalt200: '#7A85CD',
cobalt300: '#303F9F',
blue0: '#F1FBFE',
seafoam0: '#E0F7FA',
seafoam100: '#80CBC4',
seafoam200: '#80CBC4',
seafoam300: '#20594B',
olive0: '#F1F8E9',
olive100: '#C5E1A5',
olive200: '#79B03B',
olive300: '#417811',
violet100: '#E5E2F8',
violet200: '#CAC3F1',
violet300: '#B0A7EA',
violet400: '#8C7EE0',
violet500: '#705EE4',
violet600: '#533FD1',
violet700: '#4B39BC',
violet800: '#4232A7',
violet900: '#3E2F9D',
violet1000: '#32267D',
violet1100: '#251C5E',
violet1200: '#1D1649',
blue100: '#EFF8FC',
blue200: '#E6F5FB',
blue300: '#CCEBF6',
blue400: '#5ABDE1',
blue500: '#51AACB',
blue600: '#4897B4',
blue700: '#09739A',
blue800: '#367187',
blue900: '#285565',
blue1200: '#1F424F',
lavender0: '#F3E5F5',
lavender100: '#CE93D8',
lavender200: '#B45AC3',
lavender300: '#7B1FA2',
pink0: '#FCF2F2',
pink100: '#FCE4EC',
pink200: '#F8BBD0',
pink300: '#EA5791',
pink400: '#C2185B',
};

View File

@ -0,0 +1,147 @@
import colors from '@conf/theme/colorThemes/color';
import ColorTheme from '@conf/theme/colorThemes/types';
const darkTheme: ColorTheme = {
bg: colors.gray1200,
bgSurface: colors.gray1100,
bgSurfaceNewNav: colors.gray1100,
bgSurfaceDarker: colors.gray1000,
bgSurfaceBrand: colors.violet1200,
bgSurfaceBrandHover: colors.violet1100,
buttonSurfaceBrandHover: colors.violet1000,
bgDisabled: colors.gray700,
bgSurfaceError: colors.red300,
bgSurfaceErrorHover: colors.wine800,
bgSurfaceInfo: colors.blue1200,
bgSurfaceInformationHover: colors.blue800,
bgSurfaceSuccess: colors.mudgreen900,
bgSurfaceSuccessHover: colors.green300,
bgSurfaceWarning: colors.yellow800,
bgSurfaceWarningHover: colors.yellow800,
bgHover: colors.gray1100,
text: colors.gray200,
textSecondary: colors.gray300,
textTertiary: colors.gray400,
textBrand: colors.violet600,
textBrandOnBgFill: colors.gray0,
textDisabled: colors.gray600,
textError: colors.red0,
textInformation: colors.blue0,
textSuccess: colors.green0,
textWarning: colors.yellow0,
hyperlinks: colors.blue400,
icon: colors.gray600,
iconBrand: colors.violet400,
iconError: colors.red50,
iconInformation: colors.blue100,
iconSuccess: colors.green100,
iconWarning: colors.yellow200,
buttonFillBrand: colors.violet700,
buttonFillFocus: colors.violet600,
buttonSurfaceBrandFocus: colors.violet500,
buttonSurfaceSecondaryHover: colors.violet500,
radioButtonBorder: colors.gray400,
radioButtonDotFill: colors.gray400,
radioButtonDotDisabled: colors.gray500,
avatarBorderBrand: colors.violet500,
avatarBorderInformation: colors.blue600,
border: colors.gray700,
borderBrand: colors.violet600,
borderBrandFocused: colors.violet400,
borderBrandInverse: colors.violet800,
borderDisabled: colors.gray600,
borderInformation: colors.blue700,
borderInformationInverse: colors.blue400,
borderError: colors.red300,
borderSuccess: colors.green300,
borderWarning: colors.yellow800,
chartsWineHigh: colors.wine600,
chartsWineMedium: colors.wine400,
chartsWineLow: colors.wine200,
chartsWineBase: colors.wine200,
chartsPinkHigh: colors.pink300,
chartsPinkMedium: colors.pink200,
chartsPinkLow: colors.pink100,
chartsPinkBase: colors.pink0,
chartsSeafoamHigh: colors.seafoam300,
chartsSeafoamMedium: colors.seafoam200,
chartsSeafoamLow: colors.seafoam100,
chartsSeafoamBase: colors.seafoam0,
chartsTangerineHigh: colors.tangerine300,
chartsTangerineMedium: colors.tangerine200,
chartsTangerineLow: colors.tangerine100,
chartsTangerineBase: colors.tangerine0,
chartsOliveHigh: colors.darkgreen300,
chartsOliveMedium: colors.darkgreen200,
chartsOliveLow: colors.darkgreen100,
chartsOliveBase: colors.darkgreen0,
chartsBlueHigh: colors.cyan300,
chartsBlueMedium: colors.cyan200,
chartsBlueLow: colors.cyan100,
chartsBlueBase: colors.cyan00,
chartsGreenHigh: colors.mudgreen600,
chartsGreenMedium: colors.mudgreen400,
chartsGreenLow: colors.mudgreen200,
chartsGreenBase: colors.mudgreen100,
chartsBrandContrast: '#2200F9FF',
chartsBrandHigh: colors.violet600,
chartsBrandMedium: colors.violet500,
chartsBrandLow: colors.violet300,
chartsBrandBase: colors.violet200,
chartsHeatmapHigh: colors.violet900,
chartsHeatmapMedium: colors.violet500,
chartsHeatmapLow: colors.violet200,
chartsHeatmapBase: colors.gray100,
chartsBrandHighAlpha: '#2200F999',
chartsBrandMediumAlpha: '#917FFF99',
chartsBrandLowAlpha: '#B0A7EA99',
chartsBrandBaseAlpha: '#CAC3F166',
chartsInformationHigh: colors.blue700,
navBgGradientTop: '#533FD10A',
navBgGradientBottom: '#3E2F9D0A',
chartsInformationMedium: colors.blue600,
chartsInformationLow: colors.blue400,
chartsInformationBase: colors.blue300,
tagsCoralBg: colors.pink400,
tagsCoralBorder: colors.pink200,
tagsCoralText: colors.pink0,
tagsBrandBg: colors.violet600,
tagsBrandBorder: colors.violet200,
tagsBrandText: colors.violet0,
tagsAccentBg: colors.blue700,
tagsAccentBorder: colors.blue300,
tagsAccentText: colors.blue0,
tagsTangerineBg: colors.tangerine300,
tagsTangerineBorder: colors.tangerine100,
tagsTangerineText: colors.tangerine0,
tagsSeafoamBg: colors.seafoam300,
tagsSeafoamBorder: colors.seafoam100,
tagsSeafoamText: colors.seafoam0,
tagsTrueBlueBg: colors.trueblue300,
tagsTrueBlueBorder: colors.trueblue100,
tagsTrueBlueText: colors.trueblue0,
tagsCobaltBlueBg: colors.cobalt300,
tagsCobaltBlueBorder: colors.cobalt100,
tagsCobaltBlueText: colors.cobalt0,
tagsCyanBg: colors.cyan300,
tagsCyanBorder: colors.cyan100,
tagsCyanText: colors.cyan00,
tagsDeepGreenBg: colors.darkgreen300,
tagsDeepGreenBorder: colors.darkgreen100,
tagsDeepGreenText: colors.darkgreen0,
tagsOliveBg: colors.olive300,
tagsOliveBorder: colors.olive100,
tagsOliveText: colors.olive0,
tagsLavenderBg: colors.lavender300,
tagsLavenderBorder: colors.lavender100,
tagsLavenderText: colors.lavender0,
tagsBrownBg: colors.brown300,
tagsBrownBorder: colors.brown200,
tagsBrownText: colors.brown0,
tagsTrueYellowBg: colors.trueyellow300,
tagsTrueYellowBorder: colors.trueyellow100,
tagsTrueYellowIcon: colors.trueyellow200,
tagsTrueYellowText: colors.trueyellow0,
};
export default darkTheme;

View File

@ -0,0 +1,147 @@
import colors from '@conf/theme/colorThemes/color';
import ColorTheme from '@conf/theme/colorThemes/types';
const lightTheme: ColorTheme = {
bg: colors.gray0,
bgSurface: colors.gray400,
bgSurfaceNewNav: colors.gray200,
bgSurfaceDarker: colors.gray100,
bgSurfaceBrand: colors.violet0,
bgSurfaceBrandHover: colors.violet200,
buttonSurfaceBrandHover: colors.violet800,
bgDisabled: colors.gray400,
bgSurfaceError: colors.red0,
bgSurfaceErrorHover: colors.wine100,
bgSurfaceInfo: colors.blue0,
bgSurfaceInformationHover: colors.blue300,
bgSurfaceSuccess: colors.mudgreen0,
bgSurfaceSuccessHover: colors.green100,
bgSurfaceWarning: colors.yellow0,
bgSurfaceWarningHover: colors.yellow200,
bgHover: colors.gray400,
text: colors.gray800,
textSecondary: colors.gray700,
textTertiary: colors.gray600,
textBrand: colors.violet600,
textBrandOnBgFill: colors.gray0,
textDisabled: colors.gray600,
textError: colors.red300,
textInformation: colors.blue700,
textSuccess: colors.green300,
textWarning: colors.yellow800,
hyperlinks: colors.violet600,
icon: colors.gray600,
iconBrand: colors.violet500,
iconError: colors.red100,
iconInformation: colors.blue600,
iconSuccess: colors.green200,
iconWarning: colors.yellow700,
buttonFillBrand: colors.violet600,
buttonFillFocus: colors.violet600,
buttonSurfaceBrandFocus: colors.violet200,
buttonSurfaceSecondaryHover: colors.violet100,
radioButtonBorder: colors.gray600,
radioButtonDotFill: colors.gray0,
radioButtonDotDisabled: colors.gray500,
avatarBorderBrand: colors.violet100,
avatarBorderInformation: colors.blue200,
border: colors.gray100,
borderBrand: colors.violet600,
borderBrandFocused: colors.violet400,
borderBrandInverse: colors.violet0,
borderDisabled: colors.gray600,
borderInformation: colors.blue700,
borderInformationInverse: colors.blue0,
borderError: colors.red300,
borderSuccess: colors.green300,
borderWarning: colors.yellow800,
chartsWineHigh: colors.wine600,
chartsWineMedium: colors.wine400,
chartsWineLow: colors.wine200,
chartsWineBase: colors.wine200,
chartsPinkHigh: colors.pink300,
chartsPinkMedium: colors.pink200,
chartsPinkLow: colors.pink100,
chartsPinkBase: colors.pink0,
chartsSeafoamHigh: colors.seafoam300,
chartsSeafoamMedium: colors.seafoam200,
chartsSeafoamLow: colors.seafoam100,
chartsSeafoamBase: colors.seafoam0,
chartsTangerineHigh: colors.tangerine300,
chartsTangerineMedium: colors.tangerine200,
chartsTangerineLow: colors.tangerine100,
chartsTangerineBase: colors.tangerine0,
chartsOliveHigh: colors.darkgreen300,
chartsOliveMedium: colors.darkgreen200,
chartsOliveLow: colors.darkgreen100,
chartsOliveBase: colors.darkgreen0,
chartsBlueHigh: colors.cyan300,
chartsBlueMedium: colors.cyan200,
chartsBlueLow: colors.cyan100,
chartsBlueBase: colors.cyan00,
chartsGreenHigh: colors.mudgreen600,
chartsGreenMedium: colors.mudgreen400,
chartsGreenLow: colors.mudgreen200,
chartsGreenBase: colors.mudgreen100,
chartsBrandContrast: '#2200F9FF',
chartsBrandHigh: colors.violet600,
chartsBrandMedium: colors.violet500,
chartsBrandLow: colors.violet300,
chartsBrandBase: colors.violet200,
chartsHeatmapHigh: colors.violet900,
chartsHeatmapMedium: colors.violet500,
chartsHeatmapLow: colors.violet200,
chartsHeatmapBase: colors.gray100,
chartsBrandHighAlpha: '#2200F999',
chartsBrandMediumAlpha: '#917FFF99',
chartsBrandLowAlpha: '#B0A7EA99',
chartsBrandBaseAlpha: '#CAC3F166',
chartsInformationHigh: colors.blue700,
navBgGradientTop: '#705EE40A',
navBgGradientBottom: '#533FD100',
chartsInformationMedium: colors.blue600,
chartsInformationLow: colors.blue400,
chartsInformationBase: colors.blue300,
tagsCoralBg: colors.pink100,
tagsCoralBorder: colors.pink200,
tagsCoralText: colors.pink400,
tagsBrandBg: colors.violet0,
tagsBrandBorder: colors.violet200,
tagsBrandText: colors.violet600,
tagsAccentBg: colors.blue0,
tagsAccentBorder: colors.blue300,
tagsAccentText: colors.blue700,
tagsTangerineBg: colors.tangerine0,
tagsTangerineBorder: colors.tangerine100,
tagsTangerineText: colors.tangerine300,
tagsSeafoamBg: colors.seafoam0,
tagsSeafoamBorder: colors.seafoam100,
tagsSeafoamText: colors.seafoam300,
tagsTrueBlueBg: colors.trueblue0,
tagsTrueBlueBorder: colors.trueblue100,
tagsTrueBlueText: colors.trueblue300,
tagsCobaltBlueBg: colors.cobalt0,
tagsCobaltBlueBorder: colors.cobalt100,
tagsCobaltBlueText: colors.cobalt300,
tagsCyanBg: colors.cyan00,
tagsCyanBorder: colors.cyan100,
tagsCyanText: colors.cyan300,
tagsDeepGreenBg: colors.darkgreen0,
tagsDeepGreenBorder: colors.darkgreen100,
tagsDeepGreenText: colors.darkgreen300,
tagsOliveBg: colors.olive0,
tagsOliveBorder: colors.olive100,
tagsOliveText: colors.olive300,
tagsLavenderBg: colors.lavender0,
tagsLavenderBorder: colors.lavender100,
tagsLavenderText: colors.lavender300,
tagsBrownBg: colors.brown0,
tagsBrownBorder: colors.brown200,
tagsBrownText: colors.brown300,
tagsTrueYellowBg: colors.trueyellow0,
tagsTrueYellowBorder: colors.trueyellow100,
tagsTrueYellowIcon: colors.trueyellow200,
tagsTrueYellowText: colors.trueyellow300,
};
export default lightTheme;

View File

@ -0,0 +1,142 @@
export default interface ColorTheme {
bg: string;
bgSurface: string;
bgSurfaceNewNav: string;
bgSurfaceDarker: string;
bgSurfaceBrand: string;
bgSurfaceBrandHover: string;
buttonSurfaceBrandHover: string;
bgDisabled: string;
bgSurfaceError: string;
bgSurfaceErrorHover: string;
bgSurfaceInfo: string;
bgSurfaceInformationHover: string;
bgSurfaceSuccess: string;
bgSurfaceSuccessHover: string;
bgSurfaceWarning: string;
bgSurfaceWarningHover: string;
bgHover: string;
text: string;
textSecondary: string;
textTertiary: string;
textBrand: string;
textBrandOnBgFill: string;
textDisabled: string;
textError: string;
textInformation: string;
textSuccess: string;
textWarning: string;
hyperlinks: string;
icon: string;
iconBrand: string;
iconError: string;
iconInformation: string;
iconSuccess: string;
iconWarning: string;
buttonFillBrand: string;
buttonFillFocus: string;
buttonSurfaceBrandFocus: string;
buttonSurfaceSecondaryHover: string;
radioButtonBorder: string;
radioButtonDotFill: string;
radioButtonDotDisabled: string;
avatarBorderBrand: string;
avatarBorderInformation: string;
border: string;
borderBrand: string;
borderBrandFocused: string;
borderBrandInverse: string;
borderDisabled: string;
borderInformation: string;
borderInformationInverse: string;
borderError: string;
borderSuccess: string;
borderWarning: string;
chartsWineHigh: string;
chartsWineMedium: string;
chartsWineLow: string;
chartsWineBase: string;
chartsPinkHigh: string;
chartsPinkMedium: string;
chartsPinkLow: string;
chartsPinkBase: string;
chartsSeafoamHigh: string;
chartsSeafoamMedium: string;
chartsSeafoamLow: string;
chartsSeafoamBase: string;
chartsTangerineHigh: string;
chartsTangerineMedium: string;
chartsTangerineLow: string;
chartsTangerineBase: string;
chartsOliveHigh: string;
chartsOliveMedium: string;
chartsOliveLow: string;
chartsOliveBase: string;
chartsBlueHigh: string;
chartsBlueMedium: string;
chartsBlueLow: string;
chartsBlueBase: string;
chartsGreenHigh: string;
chartsGreenMedium: string;
chartsGreenLow: string;
chartsGreenBase: string;
chartsBrandContrast: string;
chartsBrandHigh: string;
chartsBrandMedium: string;
chartsBrandLow: string;
chartsBrandBase: string;
chartsHeatmapHigh: string;
chartsHeatmapMedium: string;
chartsHeatmapLow: string;
chartsHeatmapBase: string;
chartsBrandHighAlpha: string;
chartsBrandMediumAlpha: string;
chartsBrandLowAlpha: string;
chartsBrandBaseAlpha: string;
chartsInformationHigh: string;
navBgGradientTop: string;
navBgGradientBottom: string;
chartsInformationMedium: string;
chartsInformationLow: string;
chartsInformationBase: string;
tagsCoralBg: string;
tagsCoralBorder: string;
tagsCoralText: string;
tagsBrandBg: string;
tagsBrandBorder: string;
tagsBrandText: string;
tagsAccentBg: string;
tagsAccentBorder: string;
tagsAccentText: string;
tagsTangerineBg: string;
tagsTangerineBorder: string;
tagsTangerineText: string;
tagsSeafoamBg: string;
tagsSeafoamBorder: string;
tagsSeafoamText: string;
tagsTrueBlueBg: string;
tagsTrueBlueBorder: string;
tagsTrueBlueText: string;
tagsCobaltBlueBg: string;
tagsCobaltBlueBorder: string;
tagsCobaltBlueText: string;
tagsCyanBg: string;
tagsCyanBorder: string;
tagsCyanText: string;
tagsDeepGreenBg: string;
tagsDeepGreenBorder: string;
tagsDeepGreenText: string;
tagsOliveBg: string;
tagsOliveBorder: string;
tagsOliveText: string;
tagsLavenderBg: string;
tagsLavenderBorder: string;
tagsLavenderText: string;
tagsBrownBg: string;
tagsBrownBorder: string;
tagsBrownText: string;
tagsTrueYellowBg: string;
tagsTrueYellowBorder: string;
tagsTrueYellowIcon: string;
tagsTrueYellowText: string;
}

View File

@ -0,0 +1,69 @@
import light from '@conf/theme/colorThemes/light';
import { Theme } from '@conf/theme/types';
const themeV1: Theme = {
id: 'themeV1',
colors: light,
styles: {
'primary-color': '#1890ff',
'primary-color-light': '#F0F5FF',
'primary-color-dark': '#002766',
'layout-header-background': 'white',
'layout-header-color': '#434343',
'layout-body-background': 'white',
'component-background': 'white',
'body-background': 'white',
'border-color-base': '#ececec',
'text-color': 'fade(black, 85%)',
'text-color-secondary': 'fade(black, 45%)',
'heading-color': 'fade(black, 85%)',
'background-color-light': 'hsv(0, 0, 98%)',
'divider-color': 'fade(black, 6%)',
'disabled-color': 'fade(black, 25%)',
'steps-nav-arrow-color': 'fade(black, 25%)',
'homepage-background-upper-fade': '#FFFFFF',
'homepage-background-lower-fade': '#FFFFFF',
'homepage-text-color': '#434343',
'box-shadow': '0px 0px 30px 0px rgb(239 239 239)',
'box-shadow-hover': '0px 1px 0px 0.5px rgb(239 239 239)',
'box-shadow-navbar-redesign': '0 0 6px 0px rgba(93, 102, 139, 0.20)',
'border-radius-navbar-redesign': '12px',
'highlight-color': '#E6F4FF',
'highlight-border-color': '#BAE0FF',
},
assets: {
logoUrl: 'assets/logo.png',
},
content: {
title: 'DataHub',
search: {
searchbarMessage: 'Search Tables, Dashboards, People, & more...',
},
menu: {
items: [
{
label: 'Project',
path: 'https://docs.datahub.com',
shouldOpenInNewTab: true,
},
{
label: 'Docs',
path: 'https://docs.datahub.com/docs',
shouldOpenInNewTab: true,
},
{
label: 'Releases',
path: 'https://docs.datahub.com/docs/releases/',
shouldOpenInNewTab: true,
},
{
label: 'GitHub',
path: 'https://github.com/datahub-project/datahub',
shouldOpenInNewTab: true,
},
],
},
},
};
export default themeV1;

View File

@ -0,0 +1,50 @@
import light from '@conf/theme/colorThemes/light';
import { Theme } from '@conf/theme/types';
const themeV2: Theme = {
id: 'themeV2',
colors: light,
styles: {
'primary-color': '#533FD1',
'primary-color-dark': '#5C3FD1',
'primary-color-light': '#ece9f8',
'layout-header-color': '#434343',
'body-background': 'white',
'border-color-base': '#ececec',
'homepage-background-upper-fade': '#FFFFFF',
'homepage-background-lower-fade': '#FFFFFF',
'homepage-text-color': '#434343',
'box-shadow': '0px 0px 30px 0px rgb(239 239 239)',
'box-shadow-hover': '0px 1px 0px 0.5px rgb(239 239 239)',
'box-shadow-navbar-redesign': '0 0 6px 0px rgba(93, 102, 139, 0.20)',
'border-radius-navbar-redesign': '12px',
'highlight-color': '#ece9f8',
'highlight-border-color': '#07878180',
},
assets: {
logoUrl: 'assets/logo.png',
},
content: {
title: 'DataHub',
search: {
searchbarMessage: 'Find tables, dashboards, people, and more',
},
menu: {
items: [
{
label: 'DataHub Project',
path: 'https://docs.datahub.com',
shouldOpenInNewTab: true,
description: 'Explore DataHub Project website',
},
{
label: 'DataHub GitHub',
path: 'https://github.com/linkedin/datahub',
shouldOpenInNewTab: true,
},
],
},
},
};
export default themeV2;

View File

@ -0,0 +1,7 @@
import dark from '@conf/theme/colorThemes/dark';
import themeV2 from '@conf/theme/themeV2';
import { Theme } from '@conf/theme/types';
const themeV2Dark: Theme = { ...themeV2, id: 'themeV2Dark', colors: dark };
export default themeV2Dark;

View File

@ -1,4 +1,5 @@
{
"id": "theme_dark_example.config.json",
"styles": {
"primary-color": "#1890ff",
"primary-color-light": "#F0F5FF",

View File

@ -1,65 +0,0 @@
{
"styles": {
"primary-color": "#1890ff",
"primary-color-light": "#F0F5FF",
"primary-color-dark": "#002766",
"layout-header-background": "white",
"layout-header-color": "#434343",
"layout-body-background": "white",
"component-background": "white",
"body-background": "white",
"border-color-base": "#ececec",
"text-color": "fade(black, 85%)",
"text-color-secondary": "fade(black, 45%)",
"heading-color": "fade(black, 85%)",
"background-color-light": "hsv(0, 0, 98%)",
"divider-color": "fade(black, 6%)",
"disabled-color": "fade(black, 25%)",
"steps-nav-arrow-color": "fade(black, 25%)",
"homepage-background-upper-fade": "#FFFFFF",
"homepage-background-lower-fade": "#FFFFFF",
"homepage-text-color": "#434343",
"box-shadow": "0px 0px 30px 0px rgb(239 239 239)",
"box-shadow-hover": "0px 1px 0px 0.5px rgb(239 239 239)",
"box-shadow-navbar-redesign": "0 0 6px 0px rgba(93, 102, 139, 0.20)",
"border-radius-navbar-redesign": "12px",
"highlight-color": "#E6F4FF",
"highlight-border-color": "#BAE0FF"
},
"assets": {
"logoUrl": "assets/logo.png"
},
"content": {
"title": "DataHub",
"homepage": {
"homepageMessage": "Find data you can count on"
},
"search": {
"searchbarMessage": "Search Tables, Dashboards, People, & more..."
},
"menu": {
"items": [
{
"label": "Project",
"path": "https://docs.datahub.com",
"shouldOpenInNewTab": true
},
{
"label": "Docs",
"path": "https://docs.datahub.com/docs",
"shouldOpenInNewTab": true
},
{
"label": "Releases",
"path": "https://docs.datahub.com/docs/releases/",
"shouldOpenInNewTab": true
},
{
"label": "GitHub",
"path": "https://github.com/datahub-project/datahub",
"shouldOpenInNewTab": true
}
]
}
}
}

View File

@ -1,57 +0,0 @@
{
"styles": {
"primary-color": "#533FD1",
"primary-color-dark": "#5C3FD1",
"primary-color-light": "#ece9f8",
"link-color": "#533FD1",
"layout-header-background": "white",
"layout-header-color": "#434343",
"layout-body-background": "white",
"component-background": "white",
"body-background": "white",
"border-color-base": "#ececec",
"text-color": "fade(black, 85%)",
"text-color-secondary": "fade(black, 45%)",
"heading-color": "fade(black, 85%)",
"background-color-light": "hsv(0, 0, 98%)",
"divider-color": "fade(black, 6%)",
"disabled-color": "fade(black, 25%)",
"steps-nav-arrow-color": "fade(black, 25%)",
"homepage-background-upper-fade": "#FFFFFF",
"homepage-background-lower-fade": "#FFFFFF",
"homepage-text-color": "#434343",
"box-shadow": "0px 0px 30px 0px rgb(239 239 239)",
"box-shadow-hover": "0px 1px 0px 0.5px rgb(239 239 239)",
"box-shadow-navbar-redesign": "0 0 6px 0px rgba(93, 102, 139, 0.20)",
"border-radius-navbar-redesign": "12px",
"highlight-color": "#ece9f8",
"highlight-border-color": "#07878180"
},
"assets": {
"logoUrl": "assets/logo.png"
},
"content": {
"title": "DataHub",
"homepage": {
"homepageMessage": "Bring clarity to your data"
},
"search": {
"searchbarMessage": "Find tables, dashboards, people, and more"
},
"menu": {
"items": [
{
"label": "DataHub Project",
"path": "https://docs.datahub.com",
"shouldOpenInNewTab": true,
"description": "Explore DataHub Project website"
},
{
"label": "DataHub GitHub",
"path": "https://github.com/linkedin/datahub",
"shouldOpenInNewTab": true
}
]
}
}
}

View File

@ -0,0 +1,8 @@
import themeV1 from '@conf/theme/themeV1';
import themeV2 from '@conf/theme/themeV2';
import themeV2Dark from '@conf/theme/themeV2Dark';
// Add custom themes here
const themes = { themeV1, themeV2, themeV2Dark };
export default themes;

View File

@ -1,42 +1,44 @@
import { Color } from '@src/alchemy-components/theme/config';
import ColorTheme from '@conf/theme/colorThemes/types';
export type Theme = {
styles: {
'primary-color'?: string;
'layout-header-background': string;
'layout-header-color': string;
'layout-body-background': string;
'component-background': string;
'body-background': string;
'border-color-base': string;
'text-color': string;
'text-color-secondary': string;
'heading-color': string;
'background-color-light': string;
'divider-color': string;
'disabled-color': string;
'steps-nav-arrow-color': string;
'homepage-background-upper-fade': string;
'homepage-background-lower-fade': string;
'box-shadow': string;
'box-shadow-hover': string;
'highlight-color': string;
'highlight-border-color': string;
};
colors?: {
primary?: Color;
id: string;
colors: ColorTheme & {
glossaryPalette?: string[];
domainPalette?: string[];
};
styles: {
'primary-color': string;
'primary-color-light': string;
'primary-color-dark': string;
'layout-header-color': string;
'body-background': string;
'border-color-base': string;
'homepage-background-upper-fade': string;
'homepage-background-lower-fade': string;
'homepage-text-color': string;
'box-shadow': string;
'box-shadow-hover': string;
'box-shadow-navbar-redesign': string;
'border-radius-navbar-redesign': string;
'highlight-color': string;
'highlight-border-color': string;
'layout-header-background'?: string;
'layout-body-background'?: string;
'component-background'?: string;
'text-color'?: string;
'text-color-secondary'?: string;
'heading-color'?: string;
'background-color-light'?: string;
'divider-color'?: string;
'disabled-color'?: string;
'steps-nav-arrow-color'?: string;
};
assets: {
logoUrl: string;
};
content: {
title: string;
subtitle?: string;
homepage: {
homepageMessage: string;
};
search: {
searchbarMessage: string;
};

View File

@ -2,10 +2,10 @@ import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { ThemeProvider } from 'styled-components';
import defaultThemeConfig from '@conf/theme/theme_light.config.json';
import themeV2 from '@conf/theme/themeV2';
const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<ThemeProvider theme={defaultThemeConfig}>{children}</ThemeProvider>
<ThemeProvider theme={themeV2}>{children}</ThemeProvider>
);
/**

View File

@ -46,9 +46,12 @@ export default defineConfig(async ({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
process.env = { ...process.env, ...env };
const themeConfigFile = `./src/conf/theme/${process.env.REACT_APP_THEME_CONFIG}`;
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
const themeConfig = require(themeConfigFile);
let antThemeConfig: any;
if (process.env.ANT_THEME_CONFIG) {
const themeConfigFile = `./src/conf/theme/${process.env.ANT_THEME_CONFIG}`;
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
antThemeConfig = require(themeConfigFile);
}
// Setup proxy to the datahub-frontend service.
const frontendProxy = {
@ -134,7 +137,7 @@ export default defineConfig(async ({ mode }) => {
javascriptEnabled: true,
// Override antd theme variables.
// https://4x.ant.design/docs/react/customize-theme#Ant-Design-Less-variables
modifyVars: themeConfig.styles,
modifyVars: antThemeConfig,
},
},
},