mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Grafana UI: Create custom Flex Component (#73226)
This commit is contained in:
parent
e3c65d355f
commit
cb6239d913
178
packages/grafana-ui/src/components/Flex/Flex.internal.story.tsx
Normal file
178
packages/grafana-ui/src/components/Flex/Flex.internal.story.tsx
Normal 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;
|
138
packages/grafana-ui/src/components/Flex/Flex.mdx
Normal file
138
packages/grafana-ui/src/components/Flex/Flex.mdx
Normal 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} />
|
75
packages/grafana-ui/src/components/Flex/Flex.tsx
Normal file
75
packages/grafana-ui/src/components/Flex/Flex.tsx
Normal 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),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
@ -9,4 +9,4 @@
|
|||||||
* be subject to the standard policies
|
* be subject to the standard policies
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {};
|
export * from './components/Flex/Flex';
|
||||||
|
Loading…
Reference in New Issue
Block a user