Skip to content

Commit 7469bae

Browse files
authored
feat: add Grid component (#442)
1 parent db0cfd0 commit 7469bae

14 files changed

+483
-4
lines changed

.storybook/config.js

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ addDecorator(
2727
const srcStories = require.context('../src', true, /\.story\.js$/);
2828
const materialsStories = require.context('../materials', true, /\.story\.js$/);
2929
const exampleStories = require.context('../examples', true, /\.story\.js$/);
30+
const srcExampleStories = require.context(
31+
'../src',
32+
true,
33+
/\.example.story\.js$/
34+
);
3035
const philosophyStories = require.context(
3136
'../philosophy',
3237
true,
@@ -39,6 +44,7 @@ function loadStories() {
3944
materialsStories.keys().forEach(filename => materialsStories(filename));
4045
srcStories.keys().forEach(filename => srcStories(filename));
4146
exampleStories.keys().forEach(filename => exampleStories(filename));
47+
srcExampleStories.keys().forEach(filename => srcExampleStories(filename));
4248
}
4349

4450
addDecorator(IntlDecorator);

.storybook/decorators/section/section.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ const sectionStyles = {
77

88
const Section = props => <div style={sectionStyles}>{props.children}</div>;
99

10-
Section.propTypes = { children: PropTypes.element.isRequired };
10+
Section.propTypes = { children: PropTypes.node.isRequired };
1111

1212
export default Section;
File renamed without changes.
File renamed without changes.

src/components/grid/README.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Grid
2+
3+
#### Description
4+
5+
The Grid component can be used to implement layouts using CSS-Grid.
6+
7+
> [CSS Grid Layout](https://css-tricks.com/snippets/css/complete-guide-grid) is the most powerful layout system available in CSS. It is a 2-dimensional system, meaning it can handle both columns and rows, unlike flexbox which is largely a 1-dimensional system. You work with Grid Layout by applying CSS rules both to a parent element (which becomes the Grid Container) and to that elements children (which become Grid Items).
8+
9+
The component accepts all the supported properties of CSS Grid, both for the parent container and the children elements (`<Grid.Item>`).
10+
11+
#### Usage
12+
13+
> We recommend having a look at the [`grid.example.story.js`](./grid.example.story.js) to see some simple usages of the CSS Grid layout.
14+
15+
```js
16+
import { Grid } from '@commercetools-frontend/ui-kit';
17+
```
18+
19+
#### Examples
20+
21+
```js
22+
<Grid gridGap="16px" gridAutoColumns="1fr" gridTemplateColumns="repeat(3, 1fr)">
23+
<Grid.Item>{'1'}</Grid.Item>
24+
<Grid.Item>{'2'}</Grid.Item>
25+
<Grid.Item>{'3'}</Grid.Item>
26+
<Grid.Item>{'4'}</Grid.Item>
27+
<Grid.Item>{'5'}</Grid.Item>
28+
</Grid>
29+
```
30+
31+
#### Properties
32+
33+
| Props | Type | Required | Values | Default | Description |
34+
| ---------- | -------- | :------: | ------ | ------- | ------------------------------------------- |
35+
| `children` | `node` || - | - | The elements to be rendered inside the grid |
36+
| `data-*` | `string` | - | - | - | Data attributes |
37+
38+
> The component accepts all supported CSS Grid properties, [as listed here](https://css-tricks.com/snippets/css/complete-guide-grid), in **camelCase** format.
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import { storiesOf } from '@storybook/react';
3+
import { withKnobs, select, number } from '@storybook/addon-knobs';
4+
import withReadme from 'storybook-readme/with-readme';
5+
import styled from '@emotion/styled';
6+
import Section from '../../../.storybook/decorators/section';
7+
import customProperties from '../../../materials/custom-properties.json';
8+
import Spacings from '../spacings';
9+
import Text from '../typography/text';
10+
import Readme from './README.md';
11+
import Grid from './grid';
12+
13+
const createList = size =>
14+
Array.from({ length: size }).map((_, index) => index + 1);
15+
16+
const Placeholder = styled.div`
17+
display: flex;
18+
align-items: center;
19+
justify-content: center;
20+
background-color: pink;
21+
padding: ${customProperties['--spacing-16']};
22+
`;
23+
24+
const renderGridElements = () => {
25+
const elems = select('Num. of grid elements', createList(20), 6);
26+
return createList(elems).map((el, index) => (
27+
<Grid.Item key={index}>
28+
<Placeholder>{el}</Placeholder>
29+
</Grid.Item>
30+
));
31+
};
32+
33+
storiesOf('Examples|Components/Grid', module)
34+
.addDecorator(withKnobs)
35+
.addDecorator(withReadme(Readme))
36+
.add('With fixed columns', () => (
37+
<Section>
38+
<Spacings.Stack scale="l">
39+
<Text.Body tone="information">
40+
{'💁 Try resizing the window to see how the grid layout behaves.'}
41+
</Text.Body>
42+
<Grid
43+
gridGap="16px"
44+
gridAutoColumns="1fr"
45+
gridTemplateColumns="repeat(3, 1fr)"
46+
>
47+
{renderGridElements()}
48+
</Grid>
49+
</Spacings.Stack>
50+
</Section>
51+
))
52+
.add('With auto-sizing columns', () => (
53+
<Section>
54+
<Spacings.Stack scale="l">
55+
<Text.Body tone="information">
56+
{'💁 Try resizing the window to see how the grid layout behaves.'}
57+
</Text.Body>
58+
<Grid
59+
gridGap="16px"
60+
gridAutoColumns="1fr"
61+
gridTemplateColumns={`repeat(auto-fill, minmax(${number(
62+
'min column width',
63+
150
64+
)}px, 1fr))`}
65+
>
66+
{renderGridElements()}
67+
</Grid>
68+
</Spacings.Stack>
69+
</Section>
70+
));

src/components/grid/grid.js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import PropTypes from 'prop-types';
2+
import styled from '@emotion/styled';
3+
4+
const GridItem = styled.div(props => ({
5+
gridColumnStart: props.gridColumnStart,
6+
gridColumnEnd: props.gridColumnEnd,
7+
gridRowStart: props.gridRowStart,
8+
gridRowEnd: props.gridRowEnd,
9+
gridColumn: props.gridColumn,
10+
gridRow: props.gridRow,
11+
gridArea: props.gridArea,
12+
justifySelf: props.justifySelf,
13+
alignSelf: props.alignSelf,
14+
placeSelf: props.placeSelf,
15+
}));
16+
GridItem.displayName = 'GridItem';
17+
GridItem.propTypes = {
18+
children: PropTypes.node,
19+
// List based on https://css-tricks.com/snippets/css/complete-guide-grid
20+
gridColumnStart: PropTypes.string,
21+
gridColumnEnd: PropTypes.string,
22+
gridRowStart: PropTypes.string,
23+
gridRowEnd: PropTypes.string,
24+
gridColumn: PropTypes.string,
25+
gridRow: PropTypes.string,
26+
gridArea: PropTypes.string,
27+
justifySelf: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
28+
alignSelf: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
29+
placeSelf: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
30+
};
31+
32+
const Grid = styled.div(props => ({
33+
display: props.display,
34+
gridTemplateColumns: props.gridTemplateColumns,
35+
gridTemplateRows: props.gridTemplateRows,
36+
gridTemplateAreas: props.gridTemplateAreas,
37+
gridTemplate: props.gridTemplate,
38+
gridColumnGap: props.gridColumnGap,
39+
gridRowGap: props.gridRowGap,
40+
gridGap: props.gridGap,
41+
justifyItems: props.justifyItems,
42+
alignItems: props.alignItems,
43+
placeItems: props.placeItems,
44+
justifyContent: props.justifyContent,
45+
alignContent: props.alignContent,
46+
placeContent: props.placeContent,
47+
gridAutoColumns: props.gridAutoColumns,
48+
gridAutoRows: props.gridAutoRows,
49+
gridAutoFlow: props.gridAutoFlow,
50+
grid: props.grid,
51+
}));
52+
Grid.displayName = 'Grid';
53+
Grid.propTypes = {
54+
children: PropTypes.node.isRequired,
55+
// List based on https://css-tricks.com/snippets/css/complete-guide-grid
56+
display: PropTypes.oneOf(['grid', 'inline-grid']).isRequired,
57+
gridTemplateColumns: PropTypes.string,
58+
gridTemplateRows: PropTypes.string,
59+
gridTemplateAreas: PropTypes.string,
60+
gridTemplate: PropTypes.string,
61+
gridColumnGap: PropTypes.string,
62+
gridRowGap: PropTypes.string,
63+
gridGap: PropTypes.string,
64+
justifyItems: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
65+
alignItems: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
66+
placeItems: PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
67+
justifyContent: PropTypes.oneOf([
68+
'start',
69+
'end',
70+
'center',
71+
'stretch',
72+
'space-around',
73+
'space-between',
74+
'space-evenly',
75+
]),
76+
alignContent: PropTypes.oneOf([
77+
'start',
78+
'end',
79+
'center',
80+
'stretch',
81+
'space-around',
82+
'space-between',
83+
'space-evenly',
84+
]),
85+
placeContent: PropTypes.oneOf([
86+
'start',
87+
'end',
88+
'center',
89+
'stretch',
90+
'space-around',
91+
'space-between',
92+
'space-evenly',
93+
]),
94+
gridAutoColumns: PropTypes.string,
95+
gridAutoRows: PropTypes.string,
96+
gridAutoFlow: PropTypes.string,
97+
grid: PropTypes.string,
98+
};
99+
Grid.defaultProps = {
100+
display: 'grid',
101+
};
102+
// Assign GridItem as a static property of Grid
103+
Grid.Item = GridItem;
104+
105+
export default Grid;

src/components/grid/grid.spec.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import { render } from '../../test-utils';
3+
import Grid from './grid';
4+
5+
describe('data-attributes', () => {
6+
it('should pass data-attributes all way to the DOM', () => {
7+
const { container } = render(
8+
<Grid data-foo="bar">
9+
<Grid.Item />
10+
<Grid.Item />
11+
</Grid>
12+
);
13+
expect(container.querySelector("[data-foo='bar']")).toBeInTheDocument();
14+
});
15+
});

0 commit comments

Comments
 (0)