Card: Add isCompact prop and Overline sub-component (#82077)

This commit is contained in:
Joao Silva 2024-02-09 12:37:28 +00:00 committed by GitHub
parent 48b4ca8228
commit bc83d8263b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 19 deletions

View File

@ -445,6 +445,24 @@ Card can have a disabled state, effectively making it and its actions non-clicka
</Card>
</Preview>
### With overline
```jsx
<Card>
<Card.Overline>Filter option</Card.Overline>
<Card.Heading>Filter by name</Card.Heading>
<Card.Description>Filter data by query.</Card.Description>
</Card>
```
<Preview>
<Card>
<Card.Overline>Filter option</Card.Overline>
<Card.Heading>Filter by name</Card.Heading>
<Card.Description>Filter data by query.</Card.Description>
</Card>
</Preview>
### Props
<ArgTypes of={Card} />

View File

@ -24,9 +24,9 @@ const meta: Meta<typeof Card> = {
},
};
export const Basic: StoryFn<typeof Card> = ({ disabled }) => {
export const Basic: StoryFn<typeof Card> = (args) => {
return (
<Card disabled={disabled}>
<Card {...args}>
<Card.Heading>Filter by name</Card.Heading>
<Card.Description>
Filter data by query. This is useful if you are sharing the results from a different panel that has many queries
@ -36,24 +36,24 @@ export const Basic: StoryFn<typeof Card> = ({ disabled }) => {
);
};
export const AsLink: StoryFn<typeof Card> = ({ disabled }) => {
export const AsLink: StoryFn<typeof Card> = (args) => {
return (
<VerticalGroup>
<Card href="https://grafana.com" disabled={disabled}>
<Card href="https://grafana.com" {...args}>
<Card.Heading>Filter by name</Card.Heading>
<Card.Description>
Filter data by query. This is useful if you are sharing the results from a different panel that has many
queries and you want to only visualize a subset of that in this panel.
</Card.Description>
</Card>
<Card href="https://grafana.com" disabled={disabled}>
<Card href="https://grafana.com" {...args}>
<Card.Heading>Filter by name2</Card.Heading>
<Card.Description>
Filter data by query. This is useful if you are sharing the results from a different panel that has many
queries and you want to only visualize a subset of that in this panel.
</Card.Description>
</Card>
<Card href="https://grafana.com" disabled={disabled}>
<Card href="https://grafana.com" {...args}>
<Card.Heading>Production system overview</Card.Heading>
<Card.Meta>Meta tags</Card.Meta>
</Card>
@ -61,9 +61,9 @@ export const AsLink: StoryFn<typeof Card> = ({ disabled }) => {
);
};
export const WithTags: StoryFn<typeof Card> = ({ disabled }) => {
export const WithTags: StoryFn<typeof Card> = (args) => {
return (
<Card disabled={disabled}>
<Card {...args}>
<Card.Heading>Elasticsearch Custom Templated Query</Card.Heading>
<Card.Meta>Elastic Search</Card.Meta>
<Card.Tags>
@ -73,9 +73,9 @@ export const WithTags: StoryFn<typeof Card> = ({ disabled }) => {
);
};
export const WithMedia: StoryFn<typeof Card> = ({ disabled }) => {
export const WithMedia: StoryFn<typeof Card> = (args) => {
return (
<Card disabled={disabled}>
<Card {...args}>
<Card.Heading>1-ops-tools1-fallback</Card.Heading>
<Card.Meta>
Prometheus
@ -89,9 +89,9 @@ export const WithMedia: StoryFn<typeof Card> = ({ disabled }) => {
</Card>
);
};
export const WithActions: StoryFn<typeof Card> = ({ disabled }) => {
export const WithActions: StoryFn<typeof Card> = (args) => {
return (
<Card disabled={disabled}>
<Card {...args}>
<Card.Heading>1-ops-tools1-fallback</Card.Heading>
<Card.Meta>
Prometheus
@ -118,9 +118,9 @@ export const WithActions: StoryFn<typeof Card> = ({ disabled }) => {
);
};
export const Full: StoryFn<typeof Card> = ({ disabled }) => {
export const Full: StoryFn<typeof Card> = (args) => {
return (
<Card disabled={disabled}>
<Card {...args}>
<Card.Heading>Card title</Card.Heading>
<Card.Description>
Description, body text. Greetings! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
@ -179,4 +179,18 @@ export const NotSelected: StoryFn<typeof Card> = () => {
);
};
export const WithOverline: StoryFn<typeof Card> = (args) => {
return (
<Card {...args}>
<Card.Overline>Overline text above the title</Card.Overline>
<Card.Heading>Card title</Card.Heading>
<Card.Description>
Description, body text. Greetings! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat.
</Card.Description>
</Card>
);
};
export default meta;

View File

@ -5,6 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '../../themes';
import { getFocusStyles } from '../../themes/mixins';
import { Text } from '../Text/Text';
import { CardContainer, CardContainerProps, getCardContainerStyles } from './CardContainer';
@ -23,9 +24,12 @@ export interface Props extends Omit<CardContainerProps, 'disableEvents' | 'disab
/** @deprecated Use `Card.Description` instead */
description?: string;
isSelected?: boolean;
/** If true, the padding of the Card will be smaller */
isCompact?: boolean;
}
export interface CardInterface extends FC<Props> {
Overline: typeof Overline;
Heading: typeof Heading;
Tags: typeof Tags;
Figure: typeof Figure;
@ -47,7 +51,16 @@ const CardContext = React.createContext<{
*
* @public
*/
export const Card: CardInterface = ({ disabled, href, onClick, children, isSelected, className, ...htmlProps }) => {
export const Card: CardInterface = ({
disabled,
href,
onClick,
children,
isSelected,
isCompact,
className,
...htmlProps
}) => {
const hasHeadingComponent = useMemo(
() => React.Children.toArray(children).some((c) => React.isValidElement(c) && c.type === Heading),
[children]
@ -55,7 +68,7 @@ export const Card: CardInterface = ({ disabled, href, onClick, children, isSelec
const disableHover = disabled || (!onClick && !href);
const onCardClick = onClick && !disabled ? onClick : undefined;
const styles = useStyles2(getCardContainerStyles, disabled, disableHover, isSelected);
const styles = useStyles2(getCardContainerStyles, disabled, disableHover, isSelected, isCompact);
return (
<CardContainer
@ -153,6 +166,28 @@ const getHeadingStyles = (theme: GrafanaTheme2) => ({
}),
});
/** Card text to be displayed above title */
const Overline = ({ children, className }: ChildProps) => {
const styles = useStyles2(getOverlineStyles);
return (
<div className={cx(styles.overline, className)}>
{children && (
<Text color="info" weight="medium" variant="bodySmall">
{children}
</Text>
)}
</div>
);
};
Overline.displayName = 'Overline';
const getOverlineStyles = (theme: GrafanaTheme2) => ({
overline: css({
gridArea: 'Overline',
marginBottom: theme.spacing(0.5),
}),
});
const Tags = ({ children, className }: ChildProps) => {
const styles = useStyles2(getTagStyles);
return <div className={cx(styles.tagList, className)}>{children}</div>;
@ -349,6 +384,7 @@ export const getCardStyles = (theme: GrafanaTheme2) => {
};
};
Card.Overline = Overline;
Card.Heading = Heading;
Card.Tags = Tags;
Card.Figure = Figure;

View File

@ -70,7 +70,8 @@ export const getCardContainerStyles = (
theme: GrafanaTheme2,
disabled = false,
disableHover = false,
isSelected?: boolean
isSelected?: boolean,
isCompact?: boolean
) => {
const isSelectable = isSelected !== undefined;
@ -79,16 +80,17 @@ export const getCardContainerStyles = (
display: 'grid',
position: 'relative',
gridTemplateColumns: 'auto 1fr auto',
gridTemplateRows: '1fr auto auto auto',
gridTemplateRows: 'auto 1fr auto auto auto',
gridAutoColumns: '1fr',
gridAutoFlow: 'row',
gridTemplateAreas: `
"Figure Overline Tags"
"Figure Heading Tags"
"Figure Meta Tags"
"Figure Description Tags"
"Figure Actions Secondary"`,
width: '100%',
padding: theme.spacing(2),
padding: theme.spacing(isCompact ? 1 : 2),
background: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default,
marginBottom: '8px',