Skip to content

Commit

Permalink
tech: add SimpleGrid component (#6233)
Browse files Browse the repository at this point in the history
* tech: add SimpleGrid component

* feat: add align prop

* chore: add default align

* fix: remove React import + minor changes

* feat(SimpleGrid): use common gaps + add e2e tests

* CHORE: Update screenshots

* fix: fix grid item in tests

* fix: fix column count in tests

* CHORE: Update screenshots

* fix: revert gap logic

* chore: add v7 Flex gap order note

* chore: add v7 SimpleGrid gap order note

* chore: run prettier

---------

Co-authored-by: GitHub Action <[email protected]>
  • Loading branch information
BlackySoul and actions-user authored Jul 25, 2024
1 parent 7f0a169 commit 12fff65
Show file tree
Hide file tree
Showing 15 changed files with 443 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/vkui/src/components/Flex/Flex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export interface FlexProps extends HTMLAttributesWithRootRef<HTMLDivElement> {
* Отступы между элементами.
* Значение из списка предопределённых пресетов или число, которое будет приведено к пикселям.
* Через массив можно задать отступ между столбцами и строками [column, row], если они отличаются.
*
* TODO [>=7]: порядок следования будет [row, column]
*/
gap?: GapsProp;
/**
Expand Down
189 changes: 189 additions & 0 deletions packages/vkui/src/components/SimpleGrid/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
Базовый компонент для позиционирования элементов, каждый из которых занимает равное количество пространства.
Построен на базе `grid layout`. Можно указать либо `columns` (тогда пространство будет поделено на заданное количество колонок), либо `minColWidth` (тогда пространство будет распределено с учетом минимальной ширины колонки)

```jsx { "props": { "layout": false, "iframe": false } }
const halsey = {
subtitle: 'ALBUM',
header: 'Halsey – Badlands⁣',
caption: 'Blue Vinyl · EU · 2015⁣',
text: 'Badlands is the story about dreams and cruel reality...',
};

const lorde = {
subtitle: 'ALBUM',
header: 'Lorde – Melodrama',
caption: 'Blue Vinyl · EU · 2018⁣',
text: 'Lorde captures emotions like none other. Her second album is a masterful study of being a young woman, a sleek and humid pop record full of grief and hedonism, crafted with the utmost care and wisdom.',
};

const GapSelectValues = [
{
label: '2xs',
value: '2xs',
},
{
label: 'xs',
value: 'xs',
},
{
label: 's',
value: 's',
},
{
label: 'm',
value: 'm',
},
{
label: 'l',
value: 'l',
},
{
label: 'xl',
value: 'xl',
},
{
label: '2xl',
value: '2xl',
},
{
label: '3xl',
value: '3xl',
},
{
label: '4xl',
value: '4xl',
},
];

const Example = () => {
const [gap, setGap] = useState('m');
const [rowGap, setRowGap] = useState('m');
const [columnGap, setColumnGap] = useState('m');
const [columns, setColumns] = useState(3);
const [itemsCount, setItemsCount] = useState(5);
const [margin, setMargin] = useState('none');
const [complexGap, setComplexGap] = useState(false);
const [align, setAlign] = useState('stretch');
const platform = usePlatform();

return (
<div style={{ display: 'flex', flexDirection: 'row-reverse', justifyContent: 'flex-end' }}>
<div>
<SimpleGrid
align={align}
columns={columns}
gap={complexGap ? [columnGap, rowGap] : gap}
margin={margin}
>
{Array.from({ length: itemsCount }, (item, index) => {
return <ContentCard {...(index % 2 === 0 ? lorde : halsey)} />;
})}
</SimpleGrid>
</div>
<div style={{ minWidth: 200 }}>
<FormItem top={`item count: ${itemsCount}`}>
<Slider
min={1}
max={20}
step={1}
value={itemsCount}
onChange={(value) => setItemsCount(value)}
/>
</FormItem>
<FormItem top={`columns ${columns}`}>
<Slider
min={1}
max={10}
step={1}
value={columns}
onChange={(value) => setColumns(value)}
/>
</FormItem>
<FormItem top="margin">
<RadioGroup>
<Radio
name="margin"
value="none"
checked={margin === 'none'}
onChange={(e) => setMargin(e.target.value)}
>
none
</Radio>
<Radio
name="margin"
value="auto"
checked={margin === 'auto'}
onChange={(e) => setMargin(e.target.value)}
>
auto
</Radio>
<Radio
name="margin"
value="auto-inline"
checked={margin === 'auto-inline'}
onChange={(e) => setMargin(e.target.value)}
>
auto-inline
</Radio>
<Radio
name="margin"
value="auto-block"
checked={margin === 'auto-block'}
onChange={(e) => setMargin(e.target.value)}
>
auto-block
</Radio>
</RadioGroup>
</FormItem>
<FormItem top="align">
<Select
value={align}
onChange={(e) => setAlign(e.target.value || 'stretch')}
placeholder="Не выбрано"
options={[
{ label: 'start', value: 'start' },
{ label: 'end', value: 'end' },
{ label: 'center', value: 'center' },
{ label: 'stretch', value: 'stretch' },
{ label: 'baseline', value: 'baseline' },
]}
allowClearButton
/>
</FormItem>
<Checkbox value={complexGap} onChange={(e) => setComplexGap(e.target.checked)}>
Complex Gap
</Checkbox>
{!complexGap && (
<FormItem top="gap">
<Select
value={gap}
onChange={(e) => setGap(e.target.value)}
options={GapSelectValues}
/>
</FormItem>
)}
{complexGap && (
<FormItem top="row gap">
<Select
value={rowGap}
onChange={(e) => setRowGap(e.target.value)}
options={GapSelectValues}
/>
</FormItem>
)}
{complexGap && (
<FormItem top="column gap">
<Select
value={columnGap}
onChange={(e) => setColumnGap(e.target.value)}
options={GapSelectValues}
/>
</FormItem>
)}
</div>
</div>
);
};

<Example />;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ComponentPlayground, type ComponentPlaygroundProps } from '@vkui-e2e/playground-helpers';
import { SimpleGrid, type SimpleGridProps } from './SimpleGrid';

const ChildNode = () => <div style={{ height: 50, backgroundColor: 'red' }}>Grid Item</div>;

export const SimpleGridPlayground = (props: ComponentPlaygroundProps) => {
return (
<ComponentPlayground
{...props}
propSets={[
{
children: [[<ChildNode key="1" />, <ChildNode key="2" />]],
columns: [1, 2],
gap: ['m'],
margin: ['auto'],
},
{
margin: ['auto-inline', 'auto-block'],
children: [[<ChildNode key="1" />, <ChildNode key="2" />]],
gap: ['xl'],
},
{
children: [[<ChildNode key="1" />, <ChildNode key="2" />, <ChildNode key="3" />]],
gap: [[8, 16]],
columns: [2],
},
]}
>
{(props: SimpleGridProps) => <SimpleGrid {...props} />}
</ComponentPlayground>
);
};
16 changes: 16 additions & 0 deletions packages/vkui/src/components/SimpleGrid/SimpleGrid.e2e.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { test } from '@vkui-e2e/test';
import { SimpleGridPlayground } from './SimpleGrid.e2e-playground';

test.describe('SimpleGrid', () => {
test.use({
onlyForAppearances: ['light'],
});
test('Rendering', async ({
mount,
expectScreenshotClippedToContent,
componentPlaygroundProps,
}) => {
await mount(<SimpleGridPlayground {...componentPlaygroundProps} />);
await expectScreenshotClippedToContent();
});
});
45 changes: 45 additions & 0 deletions packages/vkui/src/components/SimpleGrid/SimpleGrid.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.SimpleGrid {
--vkui_internal--grid_columns: 1;
--vkui_internal--min_col_width: 0;

display: grid;
gap: var(--vkui_internal--row_gap) var(--vkui_internal--column_gap);
grid-template-columns: repeat(var(--vkui_internal--grid_columns), minmax(0, 1fr));
}

.SimpleGrid--margin-auto {
margin-inline: var(--vkui--size_base_padding_horizontal--regular);
margin-block: var(--vkui--size_base_padding_vertical--regular);
}

.SimpleGrid--margin-auto-inline {
margin-inline: var(--vkui--size_base_padding_horizontal--regular);
}

.SimpleGrid--margin-auto-block {
margin-block: var(--vkui--size_base_padding_vertical--regular);
}

.SimpleGrid--with-min-width {
grid-template-columns: repeat(auto-fit, minmax(var(--vkui_internal--min_col_width), 1fr));
}

.SimpleGrid--align-start {
align-items: flex-start;
}

.SimpleGrid--align-end {
align-items: flex-end;
}

.SimpleGrid--align-center {
align-items: center;
}

.SimpleGrid--align-stretch {
align-items: stretch;
}

.SimpleGrid--align-baseline {
align-items: baseline;
}
42 changes: 42 additions & 0 deletions packages/vkui/src/components/SimpleGrid/SimpleGrid.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Meta, StoryObj } from '@storybook/react';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { ContentCard } from '../ContentCard/ContentCard';
import { SimpleGrid, SimpleGridProps } from './SimpleGrid';

const story: Meta<SimpleGridProps> = {
title: 'Layout/SimpleGrid',
component: SimpleGrid,
parameters: { ...CanvasFullLayout, ...DisableCartesianParam },
};

export default story;

type Story = StoryObj<SimpleGridProps>;

export const Playground: Story = {
args: {
gap: 'm',
},
render: (args) => (
<SimpleGrid {...args}>
{Array.from({ length: 5 }, (_, index) => {
return (
<ContentCard
key={index}
subtitle="ALBUM"
header="Halsey – Badlands"
caption="Blue Vinyl · EU · 2015"
text="Badlands is the story about dreams and cruel reality..."
/>
);
})}
</SimpleGrid>
),
decorators: [
(Component) => (
<div style={{ width: '80%', border: '1px dotted red' }}>
<Component />
</div>
),
],
};
6 changes: 6 additions & 0 deletions packages/vkui/src/components/SimpleGrid/SimpleGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { baselineComponent } from '../../testing/utils';
import { SimpleGrid } from './SimpleGrid';

describe('SimpleGrid', () => {
baselineComponent(SimpleGrid);
});
Loading

0 comments on commit 12fff65

Please sign in to comment.