Skip to content

Commit 9d38989

Browse files
authored
grid: add grid implementation (#96597)
Add Grid layout primitive that follows the Flex conventions
1 parent 055056d commit 9d38989

File tree

7 files changed

+454
-30
lines changed

7 files changed

+454
-30
lines changed

static/app/components/core/layout/container.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ interface BaseContainerProps {
4444
border?: Responsive<Border>;
4545

4646
area?: Responsive<React.CSSProperties['gridArea']>;
47+
order?: Responsive<React.CSSProperties['order']>;
4748
}
4849
/* eslint-enable typescript-sort-keys/interface */
4950
export type ContainerElement =
@@ -77,6 +78,7 @@ const omitContainerProps = new Set<keyof ContainerProps<any>>([
7778
'overflow',
7879
'overflowX',
7980
'overflowY',
81+
'order',
8082
'position',
8183
'radius',
8284
'width',
@@ -123,6 +125,7 @@ export const Container = styled(
123125
${p => rc('max-height', p.maxHeight, p.theme)};
124126
125127
${p => rc('grid-area', p.area, p.theme)};
128+
${p => rc('order', p.order, p.theme)};
126129
127130
${p => rc('border', p.border, p.theme, getBorder)};
128131

static/app/components/core/layout/flex.mdx

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,11 @@ resources:
66
js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/flex.tsx
77
---
88

9-
import {Flex} from 'sentry/components/core/layout/flex';
9+
import {Container, Flex} from 'sentry/components/core/layout';
1010
import * as Storybook from 'sentry/stories';
1111

1212
import APIReference from '!!type-loader!sentry/components/core/layout/flex';
1313

14-
export function Card(props) {
15-
return (
16-
<div style={{padding: '8px', background: 'white', borderRadius: '4px'}}>
17-
{props.children}
18-
</div>
19-
);
20-
}
21-
2214
export const types = {Flex: APIReference.Flex};
2315

2416
The `Flex` component is a layout component that extends the `Container` component with CSS flexbox properties. It combines the powerful spacing, sizing, and styling capabilities of `Container` with flexible positioning and alignment controls.
@@ -45,9 +37,15 @@ Simialarly to `Container`, `Flex` inherits all spacing props like `m`, `p`, `mt`
4537

4638
<Storybook.Demo>
4739
<Flex direction="row" gap="md" justify="between" align="center" padding="md">
48-
<Card>Item 1</Card>
49-
<Card>Item 2</Card>
50-
<Card>Item 3</Card>
40+
<Container padding="md" border="primary" radius="md" background="primary">
41+
Item 1
42+
</Container>
43+
<Container padding="md" border="primary" radius="md" background="primary">
44+
Item 2
45+
</Container>
46+
<Container padding="md" border="primary" radius="md" background="primary">
47+
Item 3
48+
</Container>
5149
</Flex>
5250
</Storybook.Demo>
5351
```jsx
@@ -62,9 +60,15 @@ Simialarly to `Container`, `Flex` inherits all spacing props like `m`, `p`, `mt`
6260

6361
<Storybook.Demo>
6462
<Flex direction="column" gap="md" justify="between" align="center">
65-
<Card>Item 1</Card>
66-
<Card>Item 2</Card>
67-
<Card>Item 3</Card>
63+
<Container padding="md" border="primary" radius="md" background="primary">
64+
Item 1
65+
</Container>
66+
<Container padding="md" border="primary" radius="md" background="primary">
67+
Item 2
68+
</Container>
69+
<Container padding="md" border="primary" radius="md" background="primary">
70+
Item 3
71+
</Container>
6872
</Flex>
6973
</Storybook.Demo>
7074
```jsx
@@ -85,9 +89,15 @@ Similarly to `Container`, the `Flex` properties like `gap` follow the same spaci
8589
<Flex direction="column" gap="sm" key={size}>
8690
<strong>{size} gap</strong>
8791
<Flex m="md" gap={size}>
88-
<Card>Item 1</Card>
89-
<Card>Item 2</Card>
90-
<Card>Item 3</Card>
92+
<Container padding="md" border="primary" radius="md" background="primary">
93+
Item 1
94+
</Container>
95+
<Container padding="md" border="primary" radius="md" background="primary">
96+
Item 2
97+
</Container>
98+
<Container padding="md" border="primary" radius="md" background="primary">
99+
Item 3
100+
</Container>
91101
</Flex>
92102
</Flex>
93103
))}
@@ -109,22 +119,27 @@ Example of a responsive flex container that uses a static gap, but changes direc
109119

110120
<Storybook.Demo>
111121
<Flex direction={{xs: 'row-reverse', sm: 'column', md: 'row'}} gap="md" p="md">
112-
<Card>Responsive</Card>
113-
<Card>Layout</Card>
114-
<Card>Items</Card>
115-
<Card>🔥</Card>
122+
<Container padding="md" border="primary" radius="md" background="primary">
123+
Responsive
124+
</Container>
125+
<Container padding="md" border="primary" radius="md" background="primary">
126+
Layout
127+
</Container>
128+
<Container padding="md" border="primary" radius="md" background="primary">
129+
Items
130+
</Container>
131+
<Container padding="md" border="primary" radius="md" background="primary">
132+
🔥
133+
</Container>
116134
</Flex>
117135
</Storybook.Demo>
118136
```jsx
119137
<Flex
120138
// Direction = column on sm and row on md
121139
direction={{xs: 'column-reverse', sm: 'column', md: 'row'}}
122-
123-
// Gap = md on all sizes
124-
gap="md"
125-
140+
// Gap = md on all sizes
141+
gap="md"
126142
>
127-
128143
<div>Responsive</div>
129144
<div>Layout</div>
130145
<div>Items</div>

static/app/components/core/layout/flex.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const omitFlexProps = new Set<keyof FlexProps>([
1313
'align',
1414
'justify',
1515
'wrap',
16-
'order',
1716
]);
1817

1918
type FlexProps<T extends ContainerElement = 'div'> = Omit<
@@ -39,7 +38,6 @@ type FlexProps<T extends ContainerElement = 'div'> = Omit<
3938
justify?: Responsive<
4039
'start' | 'end' | 'center' | 'between' | 'around' | 'evenly' | 'left' | 'right'
4140
>;
42-
order?: Responsive<CSSProperties['order']>;
4341
wrap?: Responsive<'nowrap' | 'wrap' | 'wrap-reverse'>;
4442
};
4543

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
---
2+
title: Grid
3+
description: A flexible layout component that provides CSS grid functionality with responsive props and spacing controls.
4+
source: 'sentry/components/core/layout/grid'
5+
resources:
6+
js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/layout/grid.tsx
7+
---
8+
9+
import {Container, Grid} from 'sentry/components/core/layout';
10+
import * as Storybook from 'sentry/stories';
11+
12+
import APIReference from '!!type-loader!sentry/components/core/layout/grid';
13+
14+
export const types = {Grid: APIReference.Grid};
15+
16+
The `Grid` component is a layout component that extends the `Container` component with CSS grid properties.
17+
18+
## Basic Usage
19+
20+
To create a basic grid container, wrap elements in `<Grid>` and define columns using `columns`.
21+
22+
```jsx
23+
<Grid columns="repeat(3, 1fr)" gap="md">
24+
<div>Item 1</div>
25+
<div>Item 2</div>
26+
<div>Item 3</div>
27+
</Grid>
28+
```
29+
30+
### Grid Areas
31+
32+
<Storybook.Demo>
33+
<Grid
34+
areas={`
35+
"header header"
36+
"sidebar main"
37+
"footer footer"`}
38+
columns="100px 1fr"
39+
rows="60px 1fr 60px"
40+
gap="md"
41+
padding="md"
42+
height="300px"
43+
>
44+
<Container
45+
area="header"
46+
border="primary"
47+
padding="md"
48+
background="primary"
49+
radius="md"
50+
>
51+
Header
52+
</Container>
53+
<Container
54+
area="sidebar"
55+
border="primary"
56+
padding="md"
57+
background="primary"
58+
radius="md"
59+
>
60+
Sidebar
61+
</Container>
62+
<Container
63+
area="main"
64+
border="primary"
65+
padding="md"
66+
minWidth="300px"
67+
background="primary"
68+
radius="md"
69+
>
70+
Main Content
71+
</Container>
72+
<Container
73+
area="footer"
74+
border="primary"
75+
padding="md"
76+
background="primary"
77+
radius="md"
78+
>
79+
Footer
80+
</Container>
81+
</Grid>
82+
</Storybook.Demo>
83+
```jsx
84+
<Grid
85+
areas="
86+
header header
87+
sidebar main
88+
footer footer"
89+
columns="200px 1fr"
90+
rows="60px 1fr 60px"
91+
gap="md"
92+
>
93+
<Container area="header">Header</Container>
94+
<Container area="sidebar">Sidebar</Container>
95+
<Container area="main">Main Content</Container>
96+
<Container area="footer">Footer</Container>
97+
</Grid>
98+
```
99+
100+
### Alignment
101+
102+
Grid provides fine-grained control over alignment using `justify`, `align`, `justifyItems`, and `alignContent`.
103+
104+
<Storybook.Demo>
105+
<Grid columns="repeat(1, 1fr)" gap="md" padding="md" width="100%">
106+
{[
107+
{justify: 'start', align: 'start', label: 'start'},
108+
{justify: 'center', align: 'center', label: 'center'},
109+
{justify: 'end', align: 'end', label: 'end'},
110+
{justify: 'between', align: 'between', label: 'between'},
111+
{justify: 'around', align: 'around', label: 'around'},
112+
{justify: 'evenly', align: 'evenly', label: 'evenly'},
113+
].map(({justify, align, label}) => (
114+
<Grid key={label} columns="repeat(2, 25%)" gap="sm" justify={justify} align={align}>
115+
<Container padding="md" background="primary" radius="md" border="primary">
116+
{label}
117+
</Container>
118+
<Container padding="md" background="primary" radius="md" border="primary">
119+
alignment
120+
</Container>
121+
</Grid>
122+
))}
123+
</Grid>
124+
</Storybook.Demo>
125+
```jsx
126+
<Grid columns="repeat(2, 1fr)" justify="center" align="center">
127+
<div>Centered Item</div>
128+
<div>Another Item</div>
129+
</Grid>
130+
```
131+
132+
### Responsive Props
133+
134+
All props support responsive values using breakpoint objects. Breakpoints are: `xs`, `sm`, `md`, `lg`, `xl`, `2xl`.
135+
136+
Example of a responsive grid container that changes from single column on small screens to multiple columns on larger screens.
137+
138+
<Storybook.Demo>
139+
<Grid columns={{xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)'}} gap="md" p="md">
140+
<Container padding="md" border="primary" radius="md" background="primary">
141+
Responsive
142+
</Container>
143+
<Container padding="md" border="primary" radius="md" background="primary">
144+
Grid
145+
</Container>
146+
<Container padding="md" border="primary" radius="md" background="primary">
147+
Layout
148+
</Container>
149+
<Container padding="md" border="primary" radius="md" background="primary">
150+
Items
151+
</Container>
152+
<Container padding="md" border="primary" radius="md" background="primary">
153+
🔥
154+
</Container>
155+
<Container padding="md" border="primary" radius="md" background="primary">
156+
Awesome
157+
</Container>
158+
</Grid>
159+
</Storybook.Demo>
160+
```jsx
161+
<Grid
162+
// Single column on xs, 2 columns on sm, 3 columns on md+
163+
columns={{xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)'}}
164+
gap="md"
165+
>
166+
<Container>Responsive</Container>
167+
<Container>Grid</Container>
168+
<Container>Layout</Container>
169+
<Container>Items</Container>
170+
<Container>🔥</Container>
171+
<Container>Awesome</Container>
172+
</Grid>
173+
```
174+
175+
If a prop is not specified for a breakpoint, the value will **not** be inherited from the previous breakpoint.

0 commit comments

Comments
 (0)