Stack: Improve layout (#75144)

* stack test cases

* more example

* Make changes to Stack

* Update mdx

* Add forwardRef

* Export Vertical and Horizontal components

* Fix spelling mistake

* PR feedback

* Fix export

* horizantal and vertical to row and column

* Fix failing import

---------

Co-authored-by: Josh Hunt <joshhunt@users.noreply.github.com>
Co-authored-by: joshhunt <josh@trtr.co>
pull/75258/head^2
Tobias Skarhed 2 years ago committed by GitHub
parent cfd468bcdd
commit 10a874ba6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      packages/grafana-ui/src/components/Layout/Stack/HorizontalStack.tsx
  2. 187
      packages/grafana-ui/src/components/Layout/Stack/Stack.internal.story.tsx
  3. 16
      packages/grafana-ui/src/components/Layout/Stack/Stack.mdx
  4. 25
      packages/grafana-ui/src/components/Layout/Stack/Stack.tsx
  5. 2
      packages/grafana-ui/src/components/Layout/Stack/index.ts
  6. 2
      packages/grafana-ui/src/unstable.ts

@ -0,0 +1,17 @@
import React from 'react';
import { ThemeSpacingTokens } from '@grafana/data';
import { ResponsiveProp } from '../utils/responsiveness';
import { Stack } from './Stack';
export const HorizontalStack = React.forwardRef<
HTMLDivElement,
React.PropsWithChildren<{ gap?: ResponsiveProp<ThemeSpacingTokens> }>
>(({ children, gap = 1 }, ref) => (
<Stack ref={ref} direction="row" gap={gap}>
{children}
</Stack>
));
HorizontalStack.displayName = 'HorizontalStack';

@ -2,7 +2,12 @@ import { Meta, StoryFn } from '@storybook/react';
import React, { ReactNode } from 'react';
import { SpacingTokenControl } from '../../../utils/storybook/themeStorybookControls';
import { Alert } from '../../Alert/Alert';
import { Button } from '../../Button';
import { Card } from '../../Card/Card';
import { Text } from '../../Text/Text';
import { HorizontalStack } from './HorizontalStack';
import { Stack } from './Stack';
import mdx from './Stack.mdx';
@ -16,7 +21,7 @@ const meta: Meta<typeof Stack> = {
},
argTypes: {
gap: SpacingTokenControl,
direction: { control: 'select', options: ['row', 'row-reverse', 'column', 'column-reverse'] },
direction: { control: 'select', options: ['row', 'column'] },
},
};
@ -34,4 +39,184 @@ export const Basic: StoryFn<typeof Stack> = ({ direction = 'column', gap = 2 })
);
};
export const TestCases: StoryFn<typeof Stack> = () => {
return (
<div style={{ width: '100%' }}>
<Stack gap={4}>
<h2>Comparisons Stack vs No stack</h2>
<HorizontalStack>
<Example title="No stack">
<Button>A button</Button>
<Button>Longer button button</Button>
</Example>
<Example title="Horizontal stack">
<HorizontalStack>
<Button>A button</Button>
<Button>Longer button button</Button>
</HorizontalStack>
</Example>
<Example title="Vertical stack">
<Stack>
<Button>A button</Button>
<Button>Longer button button</Button>
</Stack>
</Example>
</HorizontalStack>
<HorizontalStack>
<Example title="No stack, mismatched heights">
<Card>
<Card.Heading>I am a card heading</Card.Heading>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description and some actions</Card.Description>
<Card.Actions>
<Button variant="secondary">Settings</Button>
<Button variant="secondary">Explore</Button>
</Card.Actions>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description!</Card.Description>
</Card>
<Button>Please press me!</Button>
</Example>
<Example title="Vertical stack, mismatched heights">
<Stack>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description and some actions</Card.Description>
<Card.Actions>
<Button variant="secondary">Settings</Button>
<Button variant="secondary">Explore</Button>
</Card.Actions>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description!</Card.Description>
</Card>
<Button>Please press me!</Button>
</Stack>
</Example>
</HorizontalStack>
<div style={{ width: 500 }}>
<Example title="No stack, too many items">
<Button>A button</Button>
<Button>Longer button button</Button>
<Button>Another button</Button>
<Button>And another</Button>
<Button>Why not - one last button!</Button>
</Example>
<Example title="Horizontal stack, too many items">
<HorizontalStack>
<Button>A button</Button>
<Button>Longer button button</Button>
<Button>Another button</Button>
<Button>And another</Button>
<Button>Why not - one last button!</Button>
</HorizontalStack>
</Example>
</div>
<h2>Child alignment</h2>
<div style={{ width: 500 }}>
<Example title="Row, mismatched heights">
<HorizontalStack>
<MyComponent>
<div style={{ height: 50, width: 100, background: 'blue' }} />
</MyComponent>
<MyComponent>
<div style={{ height: 150, width: 100, background: 'orange' }} />
</MyComponent>
</HorizontalStack>
</Example>
</div>
<Example title="Horizontal stack, mismatched heights">
<HorizontalStack>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description and some actions</Card.Description>
<Card.Actions>
<Button variant="secondary">Settings</Button>
<Button variant="secondary">Explore</Button>
</Card.Actions>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description!</Card.Description>
</Card>
</HorizontalStack>
</Example>
<Example title="Horizontal stack, mismatched heights with different components">
<HorizontalStack>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
</Card>
<Card>
<Card.Heading>I am a card heading</Card.Heading>
<Card.Description>Ohhhhh - and now a description!</Card.Description>
</Card>
<Alert severity="info" title="Plus an alert!" />
</HorizontalStack>
</Example>
<Example title="Horizontal stack, alerts with even heights">
<HorizontalStack>
<Alert severity="info" title="Plus an alert!" />
<Alert severity="success" title="Plus an alert!" />
<Alert severity="warning" title="Plus an alert!" />
<Alert severity="error" title="Plus an alert!" />
</HorizontalStack>
</Example>
<Example title="Horizontal stack, alerts with mismatched heights">
<HorizontalStack>
<Alert severity="info" title="Plus an alert!" />
<Alert severity="success" title="Plus an alert!" />
<Alert severity="warning" title="Plus an alert!">
Surprise - a description! What will happen to the height of all the other alerts?
</Alert>
<Alert severity="error" title="Plus an alert!" />
</HorizontalStack>
</Example>
</Stack>
</div>
);
};
function Example({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div>
<Text variant="h3">{title}</Text>
<div style={{ background: 'rgba(255,255,255,0.1)', border: '1px dashed green' }}>{children}</div>
</div>
);
}
function MyComponent({ children }: { children: React.ReactNode }) {
return <div style={{ background: 'rgba(0,255,255, 0.2)', padding: 16 }}>{children}</div>;
}
export default meta;

@ -1,5 +1,5 @@
import { Meta, ArgTypes, Canvas } from '@storybook/blocks';
import { Stack } from './Stack';
import { Stack, HorizontalStack } from './index';
import * as Stories from './Stack.internal.story';
<Meta title="MDX|Stack" component={Stack} />
@ -8,6 +8,8 @@ import * as Stories from './Stack.internal.story';
The `Stack` component is designed to assist with layout and positioning of elements within a container, offering a simple and flexible way to stack elements vertically or horizontally. This documentation outlines the proper usage of the Stack component and provides guidance on when to use it over the Grid or Flex components.
There is also a `HorizontalStack` component, which is a thin wrapper around Stack, equivalent to `<Stack direction="row">`.
### Usage
#### When to use
@ -28,7 +30,15 @@ Use the `Grid` component instead for these use cases:
Use the `Flex` component instead for these use cases:
- **Centering:** Perfect for centering elements both vertically and horizontally.
- **Dynamic Order:** Easily reorder elements for responsive layouts without changing code.
- **Alignment:** More options for item alignment.
- **Flex items:** Custom flex basis or configure how items stretch and wrap.
## Props
### Stack
<ArgTypes of={Stack} />
### HorizontalStack
<ArgTypes of={HorizontalStack} />

@ -2,18 +2,23 @@ import React from 'react';
import { ThemeSpacingTokens } from '@grafana/data';
import { Direction, Flex } from '../Flex/Flex';
import { Flex } from '../Flex/Flex';
import { ResponsiveProp } from '../utils/responsiveness';
interface StackProps {
direction?: ResponsiveProp<Direction>;
direction?: ResponsiveProp<'column' | 'row'>;
gap?: ResponsiveProp<ThemeSpacingTokens>;
}
export const Stack = ({ gap = 1, direction = 'column', children }: React.PropsWithChildren<StackProps>) => {
return (
<Flex gap={gap} direction={direction}>
{children}
</Flex>
);
};
export const Stack = React.forwardRef<HTMLDivElement, React.PropsWithChildren<StackProps>>(
({ gap = 1, direction = 'column', children }, ref) => {
return (
<Flex ref={ref} gap={gap} direction={direction} wrap="wrap">
{React.Children.map(children, (child) => (
<div>{child}</div>
))}
</Flex>
);
}
);
Stack.displayName = 'Stack';

@ -0,0 +1,2 @@
export { Stack } from './Stack';
export { HorizontalStack } from './HorizontalStack';

@ -12,4 +12,4 @@
export * from './components/Layout/Box/Box';
export * from './components/Layout/Flex/Flex';
export { Stack } from './components/Layout/Stack/Stack';
export { Stack, HorizontalStack } from './components/Layout/Stack';

Loading…
Cancel
Save