diff --git a/packages/grafana-ui/src/components/Flex/Flex.internal.story.tsx b/packages/grafana-ui/src/components/Flex/Flex.internal.story.tsx new file mode 100644 index 00000000000..2e933ced959 --- /dev/null +++ b/packages/grafana-ui/src/components/Flex/Flex.internal.story.tsx @@ -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 ( +
+ {text &&

{text}

} +
+ ); +}; + +const meta: Meta = { + title: 'General/Layout/Flex', + component: Flex, + decorators: [withCenteredStory], + parameters: { + docs: { + page: mdx, + }, + }, +}; + +export const Basic: StoryFn = ({ direction, wrap, alignItems, justifyContent, gap }) => { + const theme = useTheme2(); + return ( +
+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + +
+ ); +}; + +export const AlignItemsExamples: StoryFn = () => { + const theme = useTheme2(); + + return ( +
+

Align items flex-start

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + +

Align items flex-end

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + +

Align items baseline

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + +

Align items center

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + +

Align items stretch

+ + + + + + + +
+ ); +}; + +export const JustifyContentExamples: StoryFn = () => { + const theme = useTheme2(); + const justifyContentOptions: JustifyContent[] = [ + 'space-between', + 'space-around', + 'space-evenly', + 'flex-start', + 'flex-end', + 'center', + ]; + + return ( +
+ {justifyContentOptions.map((justifyContent) => ( + <> +

Justify Content {justifyContent}

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + + + ))} +
+ ); +}; + +export const GapExamples: StoryFn = () => { + const theme = useTheme2(); + const gapOptions: ThemeSpacingTokens[] = [2, 8, 10]; + return ( +
+ {gapOptions.map((gap) => ( + <> +

Gap with spacingToken set to {gap}

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + + + ))} +
+ ); +}; + +export const WrapExamples: StoryFn = () => { + const theme = useTheme2(); + const wrapOptions: Wrap[] = ['nowrap', 'wrap', 'wrap-reverse']; + return ( +
+ {wrapOptions.map((wrap) => ( + <> +

Wrap examples with {wrap} and gap set to spacingToken 2 (16px)

+ + {Array.from({ length: 10 }).map((_, i) => ( + + ))} + + + ))} +
+ ); +}; + +export const DirectionExamples: StoryFn = () => { + const theme = useTheme2(); + const directionOptions: Direction[] = ['row', 'row-reverse', 'column', 'column-reverse']; + return ( +
+ {directionOptions.map((direction) => ( + <> +

Direction {direction}

+ + {Array.from({ length: 5 }).map((_, i) => ( + + ))} + + + ))} +
+ ); +}; + +export default meta; diff --git a/packages/grafana-ui/src/components/Flex/Flex.mdx b/packages/grafana-ui/src/components/Flex/Flex.mdx new file mode 100644 index 00000000000..a8dd4a58777 --- /dev/null +++ b/packages/grafana-ui/src/components/Flex/Flex.mdx @@ -0,0 +1,138 @@ +import { Meta, ArgTypes } from '@storybook/blocks'; +import { Flex } from './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(); + +
+

Using Flex component to align-items stretch and justify-content to be center

+
+ + + + + + +``` + +- Wrap items wrap-reverse + +```ts +import { Flex } from '@grafana/ui' +import { useTheme2 } from '../../themes'; + +const theme = useTheme2(); + +
+

Using Flex component to align-items with wrap-reverse property

+
+ + + + + + +``` + +- JustifyContent flex-start + +```ts +import { Flex } from '@grafana/ui' +import { useTheme2 } from '../../themes'; + +const theme = useTheme2(); + +
+

Using Flex component to align-items with justify-content property

+
+ + + + + + +``` + +- Gap of 16px using the ThemeSpacingTokens + +```ts +import { Flex } from '@grafana/ui' +import { useTheme2 } from '../../themes'; + +const theme = useTheme2(); + +
+

Using Flex component to align-items with gap of 16px

+
+ + + + + + +``` + +- Direction column + +```ts +import { Flex } from '@grafana/ui' +import { useTheme2 } from '../../themes'; + +const theme = useTheme2(); + +
+

Using Flex component to align-items with direction column

+
+ + + + + + +``` + +### Props + + diff --git a/packages/grafana-ui/src/components/Flex/Flex.tsx b/packages/grafana-ui/src/components/Flex/Flex.tsx new file mode 100644 index 00000000000..8fa3175e463 --- /dev/null +++ b/packages/grafana-ui/src/components/Flex/Flex.tsx @@ -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
{children}
; +}; + +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), + }), + }; +}; diff --git a/packages/grafana-ui/src/unstable.ts b/packages/grafana-ui/src/unstable.ts index 4628306ac09..f882d5a8a34 100644 --- a/packages/grafana-ui/src/unstable.ts +++ b/packages/grafana-ui/src/unstable.ts @@ -9,4 +9,4 @@ * be subject to the standard policies */ -export {}; +export * from './components/Flex/Flex';