Storybook: Fixes theming of the padded story (that makes stories use primary background by default) (#33166)

This commit is contained in:
Torkel Ödegaard 2021-04-21 09:45:12 +02:00 committed by GitHub
parent bad048b7ba
commit dc6d134276
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 92 deletions

View File

@ -9,7 +9,6 @@ import '../../../public/vendor/flot/jquery.flot.crosshair';
import '../../../public/vendor/flot/jquery.flot.dashes';
import '../../../public/vendor/flot/jquery.flot.gauge';
import { withTheme } from '../src/utils/storybook/withTheme';
import { withPaddedStory } from '../src/utils/storybook/withPaddedStory';
// @ts-ignore
import lightTheme from '../../../public/sass/grafana.light.scss';
// @ts-ignore
@ -33,7 +32,7 @@ addons.setConfig({
theme: GrafanaDark,
});
export const decorators = [withTheme(handleThemeChange), withPaddedStory];
export const decorators = [withTheme(handleThemeChange)];
export const parameters = {
docs: {

View File

@ -1,5 +1,4 @@
import React from 'react';
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
import { CallToActionCard, CallToActionCardProps } from './CallToActionCard';
import { Story, Meta } from '@storybook/react';
import { Button } from '../Button/Button';
@ -37,11 +36,9 @@ export const Basic: Story<StoryProps> = (args) => {
),
};
return renderComponentWithTheme(CallToActionCard, {
message: args.message,
callToActionElement: ctaElements[args.Element],
footer: args.footer,
});
return (
<CallToActionCard message={args.message} callToActionElement={ctaElements[args.Element]} footer={args.footer} />
);
};
Basic.args = {

View File

@ -1,32 +1,27 @@
import React, { useContext } from 'react';
import React from 'react';
import { render } from 'enzyme';
import { CallToActionCard, CallToActionCardProps } from './CallToActionCard';
import { ThemeContext } from '../../themes';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
const TestRenderer = (props: Omit<CallToActionCardProps, 'theme'>) => {
const theme = useContext(ThemeContext);
return <CallToActionCard theme={theme} {...props} />;
};
import { CallToActionCard } from './CallToActionCard';
describe('CallToActionCard', () => {
describe('rendering', () => {
it('when no message and footer provided', () => {
const tree = render(<TestRenderer callToActionElement={<a href="http://dummy.link">Click me</a>} />);
const tree = render(<CallToActionCard callToActionElement={<a href="http://dummy.link">Click me</a>} />);
expect(tree).toMatchSnapshot();
});
it('when message and no footer provided', () => {
const tree = render(
<TestRenderer message="Click button bellow" callToActionElement={<a href="http://dummy.link">Click me</a>} />
<CallToActionCard
message="Click button bellow"
callToActionElement={<a href="http://dummy.link">Click me</a>}
/>
);
expect(tree).toMatchSnapshot();
});
it('when message and footer provided', () => {
const tree = render(
<TestRenderer
<CallToActionCard
message="Click button bellow"
footer="footer content"
callToActionElement={<a href="http://dummy.link">Click me</a>}

View File

@ -1,17 +1,33 @@
import React from 'react';
import { Themeable } from '../../types/theme';
import { GrafanaTheme } from '@grafana/data';
import { css, cx } from '@emotion/css';
import { stylesFactory } from '../../themes';
import { useStyles } from '../../themes/ThemeContext';
export interface CallToActionCardProps extends Themeable {
export interface CallToActionCardProps {
message?: string | JSX.Element;
callToActionElement: JSX.Element;
footer?: string | JSX.Element;
className?: string;
}
const getCallToActionCardStyles = stylesFactory((theme: GrafanaTheme) => ({
export const CallToActionCard: React.FunctionComponent<CallToActionCardProps> = ({
message,
callToActionElement,
footer,
className,
}) => {
const css = useStyles(getStyles);
return (
<div className={cx([css.wrapper, className])}>
{message && <div className={css.message}>{message}</div>}
{callToActionElement}
{footer && <div className={css.footer}>{footer}</div>}
</div>
);
};
const getStyles = (theme: GrafanaTheme) => ({
wrapper: css`
label: call-to-action-card;
padding: ${theme.spacing.lg};
@ -30,22 +46,4 @@ const getCallToActionCardStyles = stylesFactory((theme: GrafanaTheme) => ({
footer: css`
margin-top: ${theme.spacing.lg};
`,
}));
export const CallToActionCard: React.FunctionComponent<CallToActionCardProps> = ({
message,
callToActionElement,
footer,
theme,
className,
}) => {
const css = getCallToActionCardStyles(theme);
return (
<div className={cx([css.wrapper, className])}>
{message && <div className={css.message}>{message}</div>}
{callToActionElement}
{footer && <div className={css.footer}>{footer}</div>}
</div>
);
};
});

View File

@ -1,24 +0,0 @@
import React from 'react';
import { GlobalStyles, useTheme } from '../../themes';
import { RenderFunction } from '../../types';
const PaddedStory: React.FunctionComponent<{}> = ({ children }) => {
const theme = useTheme();
return (
<div
style={{
width: '100%',
padding: '20px',
display: 'flex',
minHeight: '80vh',
background: `${theme.v2.palette.background.primary}`,
}}
>
<GlobalStyles />
{children}
</div>
);
};
export const withPaddedStory = (story: RenderFunction) => <PaddedStory>{story()}</PaddedStory>;

View File

@ -1,20 +1,38 @@
import React from 'react';
import { ThemeContext } from '../../themes/ThemeContext';
import { getTheme } from '../../themes/index';
import { getTheme, GlobalStyles } from '../../themes/index';
import { GrafanaThemeType } from '@grafana/data';
import { RenderFunction } from '../../types';
import { useDarkMode } from 'storybook-dark-mode';
type SassThemeChangeHandler = (theme: GrafanaThemeType) => void;
const ThemeableStory: React.FunctionComponent<{ handleSassThemeChange: SassThemeChangeHandler }> = ({
children,
handleSassThemeChange,
}) => {
const theme = useDarkMode() ? GrafanaThemeType.Dark : GrafanaThemeType.Light;
const themeType = useDarkMode() ? GrafanaThemeType.Dark : GrafanaThemeType.Light;
handleSassThemeChange(theme);
handleSassThemeChange(themeType);
return <ThemeContext.Provider value={getTheme(theme)}>{children}</ThemeContext.Provider>;
const theme = getTheme(themeType);
return (
<ThemeContext.Provider value={theme}>
<div
style={{
width: '100%',
padding: '20px',
display: 'flex',
minHeight: '80vh',
background: `${theme.v2.palette.background.primary}`,
}}
>
<GlobalStyles />
{children}
</div>
</ThemeContext.Provider>
);
};
// Temporary solution. When we update to Storybook V5 we will be able to pass data from decorator to story

View File

@ -1,6 +1,6 @@
import React, { MouseEvent, useContext } from 'react';
import React, { MouseEvent } from 'react';
import { css } from '@emotion/css';
import { CallToActionCard, Icon, IconName, LinkButton, ThemeContext } from '@grafana/ui';
import { CallToActionCard, Icon, IconName, LinkButton } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
export interface Props {
@ -39,8 +39,6 @@ const EmptyListCTA: React.FunctionComponent<Props> = ({
infoBox,
infoBoxTitle,
}) => {
const theme = useContext(ThemeContext);
const footer = () => {
return (
<>
@ -86,15 +84,7 @@ const EmptyListCTA: React.FunctionComponent<Props> = ({
</LinkButton>
);
return (
<CallToActionCard
className={ctaStyle}
message={title}
footer={footer()}
callToActionElement={ctaElement}
theme={theme}
/>
);
return <CallToActionCard className={ctaStyle} message={title} footer={footer()} callToActionElement={ctaElement} />;
};
export default EmptyListCTA;

View File

@ -35,12 +35,6 @@ export const NoDataSourceCallToAction = () => {
`;
return (
<CallToActionCard
callToActionElement={ctaElement}
className={cardClassName}
footer={footer}
message={message}
theme={theme}
/>
<CallToActionCard callToActionElement={ctaElement} className={cardClassName} footer={footer} message={message} />
);
};