Grafana-UI: Export Card container styles (#33386)

* Export Card container styles

* Export CardContainer as a separate component

* Update story

* Remove tooltip
pull/32888/head^2
Alex Khomenko 4 years ago committed by GitHub
parent 0d1cdbe227
commit 1336a57e99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      packages/grafana-ui/src/components/Card/Card.story.tsx
  2. 115
      packages/grafana-ui/src/components/Card/Card.tsx
  3. 84
      packages/grafana-ui/src/components/Card/CardContainer.tsx
  4. 3
      packages/grafana-ui/src/components/index.ts

@ -21,13 +21,9 @@ export default {
knobs: {
disable: true,
},
},
argTypes: {
heading: { control: { disable: true } },
description: { control: { disable: true } },
href: { control: { disable: true } },
tooltip: { control: { disable: true } },
onClick: { control: { disable: true } },
controls: {
exclude: ['onClick', 'href', 'heading', 'description', 'className'],
},
},
};
@ -63,17 +59,6 @@ export const AsLink: Story<Props> = ({ disabled }) => {
);
};
export const WithTooltip: Story<Props> = ({ disabled }) => {
return (
<Card
heading="Reduce"
description="Reduce all rows or data points to a single value using a function like max, min, mean or last."
tooltip="Click to apply this transformation."
disabled={disabled}
/>
);
};
export const WithTags: Story<Props> = ({ disabled }) => {
return (
<Card heading="Elasticsearch – Custom Templated Query" disabled={disabled}>

@ -1,50 +1,13 @@
import React, { memo, cloneElement, FC, HTMLAttributes, ReactNode, useCallback } from 'react';
import React, { memo, cloneElement, FC, ReactNode, useCallback } from 'react';
import { css, cx } from '@emotion/css';
import { GrafanaThemeV2 } from '@grafana/data';
import { useTheme2, styleMixins, stylesFactory } from '../../themes';
import { Tooltip, PopoverContent } from '../Tooltip/Tooltip';
import { useTheme2, stylesFactory } from '../../themes';
import { CardContainer, CardContainerProps } from './CardContainer';
/**
* @public
*/
export interface ContainerProps extends HTMLAttributes<HTMLOrSVGElement> {
/** Content for the card's tooltip */
tooltip?: PopoverContent;
}
const CardContainer: FC<ContainerProps> = ({ children, tooltip, ...props }) => {
return tooltip ? (
<Tooltip placement="top" content={tooltip} theme="info">
<div {...props}>{children}</div>
</Tooltip>
) : (
<div {...props}>{children}</div>
);
};
/**
* @public
*/
export interface CardInnerProps {
href?: string;
}
const CardInner: FC<CardInnerProps> = ({ children, href }) => {
const theme = useTheme2();
const styles = getCardStyles(theme);
return href ? (
<a className={styles.innerLink} href={href}>
{children}
</a>
) : (
<div className={styles.innerLink}>{children}</div>
);
};
/**
* @public
*/
export interface Props extends ContainerProps {
export interface Props extends Omit<CardContainerProps, 'disableEvents' | 'disableHover'> {
/** Main heading for the Card **/
heading: ReactNode;
/** Card description text */
@ -74,7 +37,6 @@ export const Card: CardInterface = ({
heading,
description,
disabled,
tooltip,
href,
onClick,
className,
@ -100,66 +62,40 @@ export const Card: CardInterface = ({
const disableHover = disabled || (!onClick && !href);
const disableEvents = disabled && !actions;
const containerStyles = getContainerStyles(theme, disableEvents, disableHover);
const onCardClick = useCallback(() => (disableHover ? () => {} : onClick?.()), [disableHover, onClick]);
return (
<CardContainer
tooltip={tooltip}
tabIndex={disableHover ? undefined : 0}
className={cx(containerStyles, className)}
onClick={onCardClick}
disableEvents={disableEvents}
disableHover={disableHover}
href={href}
{...htmlProps}
>
<CardInner href={href}>
{figure}
<div className={styles.inner}>
<div className={styles.info}>
<div>
<div className={styles.heading} role="heading">
{heading}
</div>
{meta}
{description && <p className={styles.description}>{description}</p>}
{figure}
<div className={styles.inner}>
<div className={styles.info}>
<div>
<div className={styles.heading} role="heading">
{heading}
</div>
{tags}
{meta}
{description && <p className={styles.description}>{description}</p>}
</div>
{hasActions && (
<div className={styles.actionRow}>
{actions}
{secondaryActions}
</div>
)}
{tags}
</div>
</CardInner>
{hasActions && (
<div className={styles.actionRow}>
{actions}
{secondaryActions}
</div>
)}
</div>
</CardContainer>
);
};
const getContainerStyles = stylesFactory((theme: GrafanaThemeV2, disabled = false, disableHover = false) => {
return css({
display: 'flex',
width: '100%',
background: theme.colors.background.secondary,
borderRadius: theme.shape.borderRadius(),
position: 'relative',
pointerEvents: disabled ? 'none' : 'auto',
marginBottom: theme.spacing(1),
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
...(!disableHover && {
'&:hover': {
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
cursor: 'pointer',
zIndex: 1,
},
'&:focus': styleMixins.getFocusStyles(theme),
}),
});
});
/**
* @public
*/
@ -245,11 +181,6 @@ export const getCardStyles = stylesFactory((theme: GrafanaThemeV2) => {
separator: css`
margin: 0 ${theme.spacing(1)};
`,
innerLink: css`
display: flex;
width: 100%;
padding: ${theme.spacing(2)};
`,
tagList: css`
max-width: 50%;
`,

@ -0,0 +1,84 @@
import React, { HTMLAttributes, ReactNode } from 'react';
import { css, cx } from '@emotion/css';
import { GrafanaThemeV2 } from '@grafana/data';
import { styleMixins, stylesFactory, useTheme2 } from '../../themes';
/**
* @public
*/
export interface CardInnerProps {
href?: string;
children?: ReactNode;
}
const CardInner = ({ children, href }: CardInnerProps) => {
const theme = useTheme2();
const { inner } = getCardContainerStyles(theme);
return href ? (
<a className={inner} href={href}>
{children}
</a>
) : (
<div className={inner}>{children}</div>
);
};
/**
* @public
*/
export interface CardContainerProps extends HTMLAttributes<HTMLOrSVGElement>, CardInnerProps {
/** Disable pointer events for the Card, e.g. click events */
disableEvents?: boolean;
/** No style change on hover */
disableHover?: boolean;
/** Custom container styles */
className?: string;
}
export const CardContainer = ({
href,
children,
disableEvents,
disableHover,
className,
...props
}: CardContainerProps) => {
const theme = useTheme2();
const { container } = getCardContainerStyles(theme, disableEvents, disableHover);
return (
<div {...props} className={cx(container, className)}>
<CardInner href={href}>{children}</CardInner>
</div>
);
};
const getCardContainerStyles = stylesFactory((theme: GrafanaThemeV2, disabled = false, disableHover = false) => {
return {
container: css({
display: 'flex',
width: '100%',
background: theme.colors.background.secondary,
borderRadius: theme.shape.borderRadius(),
position: 'relative',
pointerEvents: disabled ? 'none' : 'auto',
marginBottom: theme.spacing(1),
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
...(!disableHover && {
'&:hover': {
background: theme.colors.emphasize(theme.colors.background.secondary, 0.03),
cursor: 'pointer',
zIndex: 1,
},
'&:focus': styleMixins.getFocusStyles(theme),
}),
}),
inner: css({
display: 'flex',
width: '100%',
padding: theme.spacing(2),
}),
};
});

@ -189,7 +189,8 @@ export { Checkbox } from './Forms/Checkbox';
export { TextArea } from './TextArea/TextArea';
export { FileUpload } from './FileUpload/FileUpload';
export { TimeRangeInput } from './TimePicker/TimeRangeInput';
export { Card, Props as CardProps, ContainerProps, CardInnerProps, getCardStyles } from './Card/Card';
export { Card, Props as CardProps, getCardStyles } from './Card/Card';
export { CardContainer, CardContainerProps } from './Card/CardContainer';
export { FormattedValueDisplay } from './FormattedValueDisplay/FormattedValueDisplay';
export { ButtonSelect } from './Dropdown/ButtonSelect';

Loading…
Cancel
Save