basic theme playground

This commit is contained in:
Ashley Harrison 2025-10-02 13:06:49 +01:00
parent db09f1134f
commit 34a9f4f2a1
No known key found for this signature in database
GPG Key ID: FFB870B48A9457E0
3 changed files with 70 additions and 11 deletions

View File

@ -18,17 +18,20 @@ import { Icon } from '../Icon/Icon';
import { Input } from '../Input/Input';
import { BackgroundColor, BorderColor, Box, BoxShadow } from '../Layout/Box/Box';
import { Stack } from '../Layout/Stack/Stack';
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
import { Switch } from '../Switch/Switch';
import { Text, TextProps } from '../Text/Text';
interface DemoBoxProps {
bg?: BackgroundColor;
border?: BorderColor;
scrollable?: boolean;
shadow?: BoxShadow;
textColor?: TextProps['color'];
}
const DemoBox = ({ bg, border, children, shadow }: React.PropsWithChildren<DemoBoxProps>) => {
const DemoBox = ({ bg, border, children, shadow, scrollable }: React.PropsWithChildren<DemoBoxProps>) => {
const MaybeScroll = scrollable ? ScrollContainer : React.Fragment;
return (
<Box
backgroundColor={bg ? bg : undefined}
@ -38,7 +41,7 @@ const DemoBox = ({ bg, border, children, shadow }: React.PropsWithChildren<DemoB
boxShadow={shadow}
borderRadius={'default'}
>
{children}
<MaybeScroll>{children}</MaybeScroll>
</Box>
);
};
@ -121,7 +124,7 @@ export const ThemeDemo = () => {
</DemoBox>
</CollapsableSection>
<CollapsableSection label="Text colors" isOpen={true}>
<Stack justifyContent="flex-start">
<Stack justifyContent="flex-start" wrap="wrap">
<DemoBox>
<TextColors t={t} />
</DemoBox>
@ -134,7 +137,7 @@ export const ThemeDemo = () => {
</Stack>
</CollapsableSection>
<CollapsableSection label="Rich colors" isOpen={true}>
<DemoBox bg="primary">
<DemoBox bg="primary" scrollable>
<table className={colorsTableStyle}>
<thead>
<tr>
@ -154,7 +157,7 @@ export const ThemeDemo = () => {
</DemoBox>
</CollapsableSection>
<CollapsableSection label="Viz hues" isOpen={true}>
<DemoBox bg="primary">
<DemoBox bg="primary" scrollable>
<table className={colorsTableStyle}>
<thead>
<tr>
@ -229,7 +232,7 @@ export const ThemeDemo = () => {
<CollapsableSection label="Buttons" isOpen={true}>
<DemoBox bg="primary">
<Stack direction="column" gap={3}>
<Stack>
<Stack wrap="wrap">
{allButtonVariants.map((variant) => (
<Button variant={variant} key={variant}>
{variant}
@ -353,6 +356,7 @@ export function RichColorDemo({ theme, color }: RichColorDemoProps) {
const colorsTableStyle = css({
textAlign: 'center',
overflow: 'auto',
td: {
padding: '8px',

View File

@ -21,6 +21,10 @@ export const ThemeProvider = ({ children, value }: { children: React.ReactNode;
return () => sub.unsubscribe();
}, []);
useEffect(() => {
setTheme(value);
}, [value]);
return (
<ThemeContext.Provider value={theme}>
<SkeletonTheme

View File

@ -1,8 +1,15 @@
import { getThemeById } from '@grafana/data';
import { css } from '@emotion/css';
import { useState } from 'react';
import { AppEvents, createTheme, GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
import { useChromeHeaderHeight } from '@grafana/runtime';
import { CodeEditor, Stack, useStyles2 } from '@grafana/ui';
import { ThemeDemo } from '@grafana/ui/internal';
import { Page } from 'app/core/components/Page/Page';
import tron from '../../../../packages/grafana-data/src/themes/themeDefinitions/tron';
import appEvents from '../../core/app_events';
import { HOME_NAV_ID } from '../../core/reducers/navModel';
import { getNavModel } from '../../core/selectors/navModel';
import { ThemeProvider } from '../../core/utils/ConfigProvider';
@ -15,7 +22,19 @@ export default function ThemePlayground() {
text: t('theme-playground.title', 'Theme playground'),
parentItem: homeNav,
};
const theme = getThemeById('tron');
const [theme, setTheme] = useState(tron);
const chromeHeaderHeight = useChromeHeaderHeight();
const styles = useStyles2(getStyles, chromeHeaderHeight);
const onBlur = (value: string) => {
try {
const themeInput = JSON.parse(value);
setTheme(themeInput);
} catch (error) {
appEvents.emit(AppEvents.alertError, ['Failed to modify theme', error]);
}
};
return (
<Page
navModel={{
@ -23,9 +42,41 @@ export default function ThemePlayground() {
main: navModel,
}}
>
<ThemeProvider value={theme}>
<ThemeDemo />
</ThemeProvider>
<Stack
direction={{
xs: 'column',
md: 'row',
}}
height="100%"
>
<CodeEditor
width="100%"
value={JSON.stringify(theme, null, 2)}
language="json"
showLineNumbers={true}
showMiniMap={true}
containerStyles={styles.codeEditor}
onBlur={onBlur}
/>
<ThemeProvider value={createTheme(theme)}>
<ThemeDemo />
</ThemeProvider>
</Stack>
</Page>
);
}
const getStyles = (theme: GrafanaTheme2, chromeHeaderHeight: number | undefined) => ({
codeEditor: css({
height: '40vh',
minWidth: '300px',
position: 'sticky',
top: chromeHeaderHeight ? `calc(${chromeHeaderHeight}px + ${theme.spacing(1)})` : 0,
width: '100%',
zIndex: theme.zIndex.activePanel,
[theme.breakpoints.up('md')]: {
height: `calc(90vh - ${chromeHeaderHeight ?? 0}px - ${theme.spacing(2)})`,
width: '70%',
},
}),
});