Grafana UI: Create custom Flex Component (#73226)

This commit is contained in:
RoxanaAnamariaTurc 2023-08-24 15:05:43 +01:00 committed by GitHub
parent e3c65d355f
commit cb6239d913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 392 additions and 1 deletions

View File

@ -0,0 +1,178 @@
import { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { ThemeSpacingTokens } from '@grafana/data';
import { useTheme2 } from '../../themes';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { Flex, JustifyContent, Wrap, Direction } from './Flex';
import mdx from './Flex.mdx';
const Item = ({ color, text, height }: { color: string; text?: string | number; height?: string }) => {
return (
<div
style={{
width: '5em',
height: height,
backgroundColor: color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{text && <h3 style={{ color: 'black' }}>{text}</h3>}
</div>
);
};
const meta: Meta<typeof Flex> = {
title: 'General/Layout/Flex',
component: Flex,
decorators: [withCenteredStory],
parameters: {
docs: {
page: mdx,
},
},
};
export const Basic: StoryFn<typeof Flex> = ({ direction, wrap, alignItems, justifyContent, gap }) => {
const theme = useTheme2();
return (
<div style={{ width: '600px', height: '600px', border: '1px solid grey' }}>
<Flex direction={direction} wrap={wrap} alignItems={alignItems} justifyContent={justifyContent} gap={gap}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.warning.main} text={i + 1} />
))}
</Flex>
</div>
);
};
export const AlignItemsExamples: StoryFn<typeof Flex> = () => {
const theme = useTheme2();
return (
<div style={{ width: '600px' }}>
<p>Align items flex-start</p>
<Flex direction="row" wrap="wrap" alignItems="flex-start" justifyContent="start" gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.error.main} text={i + 1} />
))}
</Flex>
<p>Align items flex-end</p>
<Flex direction="row" wrap="wrap" alignItems="flex-end" justifyContent="end" gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.error.main} text={i + 1} />
))}
</Flex>
<p>Align items baseline</p>
<Flex direction="row" wrap="nowrap" alignItems="baseline" justifyContent="center" gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.error.main} text={i + 1} />
))}
</Flex>
<p>Align items center</p>
<Flex direction="row" wrap="wrap" alignItems="center" justifyContent="center" gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.error.main} text={i + 1} />
))}
</Flex>
<p>Align items stretch</p>
<Flex direction="row" wrap="wrap" alignItems="stretch" justifyContent="center" gap={2}>
<Item color={theme.colors.error.main} height="10em" />
<Item color={theme.colors.error.main} />
<Item color={theme.colors.error.main} height="3em" />
<Item color={theme.colors.error.main} />
<Item color={theme.colors.error.main} />
</Flex>
</div>
);
};
export const JustifyContentExamples: StoryFn<typeof Flex> = () => {
const theme = useTheme2();
const justifyContentOptions: JustifyContent[] = [
'space-between',
'space-around',
'space-evenly',
'flex-start',
'flex-end',
'center',
];
return (
<div style={{ width: '600px' }}>
{justifyContentOptions.map((justifyContent) => (
<>
<p>Justify Content {justifyContent}</p>
<Flex direction="row" wrap="wrap" alignItems="center" justifyContent={justifyContent} gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.warning.main} text={i + 1} />
))}
</Flex>
</>
))}
</div>
);
};
export const GapExamples: StoryFn<typeof Flex> = () => {
const theme = useTheme2();
const gapOptions: ThemeSpacingTokens[] = [2, 8, 10];
return (
<div style={{ width: '800px' }}>
{gapOptions.map((gap) => (
<>
<p>Gap with spacingToken set to {gap}</p>
<Flex direction="row" wrap="wrap" alignItems="flex-start" justifyContent="flex-start" gap={gap}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.error.main} text={i + 1} />
))}
</Flex>
</>
))}
</div>
);
};
export const WrapExamples: StoryFn<typeof Flex> = () => {
const theme = useTheme2();
const wrapOptions: Wrap[] = ['nowrap', 'wrap', 'wrap-reverse'];
return (
<div style={{ width: '600px' }}>
{wrapOptions.map((wrap) => (
<>
<p>Wrap examples with {wrap} and gap set to spacingToken 2 (16px)</p>
<Flex direction="row" wrap={wrap} alignItems="center" justifyContent="center" gap={2}>
{Array.from({ length: 10 }).map((_, i) => (
<Item key={i} color={theme.colors.warning.main} text={i + 1} />
))}
</Flex>
</>
))}
</div>
);
};
export const DirectionExamples: StoryFn<typeof Flex> = () => {
const theme = useTheme2();
const directionOptions: Direction[] = ['row', 'row-reverse', 'column', 'column-reverse'];
return (
<div style={{ width: '600px' }}>
{directionOptions.map((direction) => (
<>
<p>Direction {direction}</p>
<Flex direction={direction} wrap="wrap" alignItems="center" justifyContent="center" gap={2}>
{Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.warning.main} text={i + 1} />
))}
</Flex>
</>
))}
</div>
);
};
export default meta;

View File

@ -0,0 +1,138 @@
import { Meta, ArgTypes } from '@storybook/blocks';
import { Flex } from './Flex';
<Meta title="MDX|Flex" component={Flex} />
# Flex
The Flex Component aims at providing a more efficient way to lay out, align and distribute space among items in a container and
the decision to create it is to ensure consistency in design across Grafana.
### Usage
#### When to use
Use when in need to align components and small parts of the application. Use as parent container to wrap elements that you wish to align in a certain way.
Also:
* when working with one dimension layout
* to display the direction of the elements
* to set the elements to wrap
* to align items (vertically or horizontally)
#### When not to use
When you need to lay out bigger parts of the application or when you want to create page lay out.
Also:
* for complex grid layouts with various rows and columns
* bidirectional layouts
* complex nesting
* equal height columns
### Variants
Flex component has few variants that can be used based on the desired alignment you need for your case.
Some examples of how to use the Flex component can be seen below:
- AlignItems stretch
```ts
import { Flex } from '@grafana/ui'
import { useTheme2 } from '../../themes';
const theme = useTheme2();
<header>
<h1>Using Flex component to align-items stretch and justify-content to be center</h1>
</header>
<Flex direction="row" wrap="wrap" alignItems="stretch" justifyContent="center" gap={2}>
<Item color={theme.colors.warning.main} height="10em" />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} height="3em" />
<Item color={theme.colors.warning.main} />
</Flex>
```
- Wrap items wrap-reverse
```ts
import { Flex } from '@grafana/ui'
import { useTheme2 } from '../../themes';
const theme = useTheme2();
<header>
<h1>Using Flex component to align-items with wrap-reverse property</h1>
</header>
<Flex direction="row" wrap="wrap-reverse" alignItems="center" justifyContent="center" gap={4}>
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
</Flex>
```
- JustifyContent flex-start
```ts
import { Flex } from '@grafana/ui'
import { useTheme2 } from '../../themes';
const theme = useTheme2();
<header>
<h1>Using Flex component to align-items with justify-content property</h1>
</header>
<Flex direction="row" wrap="wrap" alignItems="center" justifyContent="flex-start" gap={2}>
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
</Flex>
```
- Gap of 16px using the ThemeSpacingTokens
```ts
import { Flex } from '@grafana/ui'
import { useTheme2 } from '../../themes';
const theme = useTheme2();
<header>
<h1>Using Flex component to align-items with gap of 16px</h1>
</header>
<Flex direction="row" wrap="wrap" alignItems="center" justifyContent="center" gap={2}>
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
</Flex>
```
- Direction column
```ts
import { Flex } from '@grafana/ui'
import { useTheme2 } from '../../themes';
const theme = useTheme2();
<header>
<h1>Using Flex component to align-items with direction column</h1>
</header>
<Flex direction="column" wrap="wrap" alignItems="center" justifyContent="center" gap={2}>
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
<Item color={theme.colors.warning.main} />
</Flex>
```
### Props
<ArgTypes of={Flex} />

View File

@ -0,0 +1,75 @@
import { css } from '@emotion/css';
import React, { useCallback } from 'react';
import { GrafanaTheme2, ThemeSpacingTokens } from '@grafana/data';
import { useStyles2 } from '../../themes';
export type AlignItems =
| 'stretch'
| 'flex-start'
| 'flex-end'
| 'center'
| 'baseline'
| 'start'
| 'end'
| 'self-start'
| 'self-end';
export type JustifyContent =
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-between'
| 'space-around'
| 'space-evenly'
| 'start'
| 'end'
| 'left'
| 'right';
export type Direction = 'row' | 'row-reverse' | 'column' | 'column-reverse';
export type Wrap = 'nowrap' | 'wrap' | 'wrap-reverse';
interface FlexProps {
gap?: ThemeSpacingTokens;
alignItems?: AlignItems;
justifyContent?: JustifyContent;
direction?: Direction;
wrap?: Wrap;
children?: React.ReactNode;
}
export const Flex = ({ gap = 1, alignItems, justifyContent, direction, wrap, children }: FlexProps) => {
const styles = useStyles2(
useCallback(
(theme) => getStyles(theme, gap, alignItems, justifyContent, direction, wrap),
[gap, alignItems, justifyContent, direction, wrap]
)
);
return <div className={styles.flex}>{children}</div>;
};
Flex.displayName = 'Flex';
const getStyles = (
theme: GrafanaTheme2,
gap: ThemeSpacingTokens,
alignItems: FlexProps['alignItems'],
justifyContent: FlexProps['justifyContent'],
direction: FlexProps['direction'],
wrap: FlexProps['wrap']
) => {
return {
flex: css({
display: 'flex',
flexDirection: direction,
flexWrap: wrap,
alignItems: alignItems,
justifyContent: justifyContent,
gap: theme.spacing(gap),
}),
};
};

View File

@ -9,4 +9,4 @@
* be subject to the standard policies
*/
export {};
export * from './components/Flex/Flex';