Skip to content

Commit

Permalink
feat: create table components
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed Feb 22, 2024
1 parent 9f2d05c commit 4653c78
Show file tree
Hide file tree
Showing 33 changed files with 971 additions and 2 deletions.
124 changes: 124 additions & 0 deletions packages/vkui/src/components/Table/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
```jsx { "props": { "layout": false, "adaptivity": true } }
const numberFormatter = new Intl.NumberFormat('ru-RU');

const currencyFormatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0,
});

const rows = new Array(30).fill(undefined).map(() => {
return {
name: getRandomString(getRandomInt(10, 20)),
status: getRandomInt(0, 1) === 1 ? 'active' : 'disabled',
budget: getRandomInt(1000, 100000),
spent: getRandomInt(10, 10000),
result: getRandomInt(5, 150),
costOfResult: getRandomInt(50, 100),
};
});

const getTotalSpent = () => rows.reduce((total, { spent }) => total + spent, 0);

const styleContainer = { display: 'flex', alignItems: 'center', gap: 8 };

const Status = ({ status = 'active', ...restProps }) => {
switch (status) {
case 'active':
return (
<div style={styleContainer} {...restProps}>
<Badge mode="new" />
<Subhead>Действующий</Subhead>
</div>
);
case 'disabled':
return (
<div style={styleContainer} {...restProps}>
<Badge mode="prominent" />
<Subhead>Неактивный</Subhead>
</div>
);
default:
return null;
}
};

const Example = () => {
return (
<Table style={{ minWidth: 650 }} aria-label="simple table" {...props}>
<TableHeader isSticky>
<TableRow>
<TableCell>
<Caption>Название объявления</Caption>
</TableCell>
<TableCell>
<Caption>Статус</Caption>
</TableCell>
<TableCell align="right">
<Caption>Бюджет</Caption>
</TableCell>
<TableCell align="right">
<Caption>Потрачено</Caption>
</TableCell>
<TableCell align="right">
<Caption>Результат</Caption>
</TableCell>
<TableCell align="right">
<Caption>Цена за рез-т</Caption>
</TableCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell asHeader scope="row">
<div style={styleContainer}>
<Image src={getAvatarUrl()} size={32} alt={row.name} />{' '}
<Subhead>{row.name}</Subhead>
</div>
</TableCell>
<TableCell>
<Status status={row.status} />
</TableCell>
<TableCell align="right">
<Subhead>
{numberFormatter.format(row.budget)} <Caption>дневной</Caption>
</Subhead>
</TableCell>
<TableCell align="right">
<Subhead>{currencyFormatter.format(row.spent)}</Subhead>
</TableCell>
<TableCell align="right">
<Subhead>{row.result}</Subhead> <Caption>установки</Caption>
</TableCell>
<TableCell align="right">
<Subhead>{currencyFormatter.format(row.costOfResult)}</Subhead>{' '}
<Caption>установки</Caption>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter isSticky>
<TableRow>
<TableCell>
<Caption>Всего {rows.length} объявлений</Caption>
</TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell align="right">
<Caption>{currencyFormatter.format(getTotalSpent())}</Caption>
</TableCell>
<TableCell align="right">
<Caption>ср. 134</Caption>
</TableCell>
<TableCell align="right">
<Caption>ср. 123</Caption>
</TableCell>
</TableRow>
</TableFooter>
</Table>
);
};

<Example />;
```
7 changes: 7 additions & 0 deletions packages/vkui/src/components/Table/Table.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.Table {
position: relative;
inline-size: 100%;
border-collapse: separate;
border-spacing: 0;
min-inline-size: 700px;
}
152 changes: 152 additions & 0 deletions packages/vkui/src/components/Table/Table.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import * as React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { getRandomInt, getRandomString } from '@vkontakte/vkjs';
import { DisableCartesianParam } from '../../storybook/constants';
import { getAvatarUrl } from '../../testing/mock';
import { Badge } from '../Badge/Badge';
import { Image } from '../Image/Image';
import { TableBody } from '../TableBody/TableBody';
import { TableCell } from '../TableCell/TableCell';
import { TableFooter } from '../TableFooter/TableFooter';
import { TableHeader } from '../TableHeader/TableHeader';
import { TableRow } from '../TableRow/TableRow';
import { Caption } from '../Typography/Caption/Caption';
import { Subhead } from '../Typography/Subhead/Subhead';
import { Table, type TableProps } from './Table';

const story: Meta<TableProps> = {
title: 'Layout/Table',
component: Table,
parameters: { layout: 'fullscreen', ...DisableCartesianParam },
};

export default story;

const numberFormatter = new Intl.NumberFormat('ru-RU');

const currencyFormatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0,
});

const rows = new Array(30).fill(undefined).map(() => {
return {
name: getRandomString(getRandomInt(10, 20)),
status: getRandomInt(0, 1) === 1 ? 'active' : 'disabled',
budget: getRandomInt(1000, 100000),
spent: getRandomInt(10, 10000),
result: getRandomInt(5, 150),
costOfResult: getRandomInt(50, 100),
};
});

const getTotalSpent = () => rows.reduce((total, { spent }) => total + spent, 0);

const styleContainer = { display: 'flex', alignItems: 'center', gap: 8 };

const Status = ({ status = 'active', ...restProps }) => {
switch (status) {
case 'active':
return (
<div style={styleContainer} {...restProps}>
<Badge mode="new" />
<Subhead>Действующий</Subhead>
</div>
);
case 'disabled':
return (
<div style={styleContainer} {...restProps}>
<Badge mode="prominent" />
<Subhead>Неактивный</Subhead>
</div>
);
default:
return null;
}
};

type Story = StoryObj<TableProps>;

export const Playground: Story = {
render(props) {
return (
<div>
<div></div>
<Table style={{ minWidth: 650 }} aria-label="simple table" {...props}>
<TableHeader isSticky>
<TableRow>
<TableCell>
<Caption>Название объявления</Caption>
</TableCell>
<TableCell>
<Caption>Статус</Caption>
</TableCell>
<TableCell align="right">
<Caption>Бюджет</Caption>
</TableCell>
<TableCell align="right">
<Caption>Потрачено</Caption>
</TableCell>
<TableCell align="right">
<Caption>Результат</Caption>
</TableCell>
<TableCell align="right">
<Caption>Цена за рез-т</Caption>
</TableCell>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell asHeader scope="row">
<div style={styleContainer}>
<Image src={getAvatarUrl()} size={32} alt={row.name} />{' '}
<Subhead>{row.name}</Subhead>
</div>
</TableCell>
<TableCell>
<Status status={row.status} />
</TableCell>
<TableCell align="right">
<Subhead>
{numberFormatter.format(row.budget)} <Caption>дневной</Caption>
</Subhead>
</TableCell>
<TableCell align="right">
<Subhead>{currencyFormatter.format(row.spent)}</Subhead>
</TableCell>
<TableCell align="right">
<Subhead>{row.result}</Subhead> <Caption>установки</Caption>
</TableCell>
<TableCell align="right">
<Subhead>{currencyFormatter.format(row.costOfResult)}</Subhead>{' '}
<Caption>установки</Caption>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter isSticky>
<TableRow>
<TableCell>
<Caption>Всего {rows.length} объявлений</Caption>
</TableCell>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell align="right">
<Caption>{currencyFormatter.format(getTotalSpent())}</Caption>
</TableCell>
<TableCell align="right">
<Caption>ср. 134</Caption>
</TableCell>
<TableCell align="right">
<Caption>ср. 123</Caption>
</TableCell>
</TableRow>
</TableFooter>
</Table>
<div></div>
</div>
);
},
};
35 changes: 35 additions & 0 deletions packages/vkui/src/components/Table/Table.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import { baselineComponent } from '../../testing/utils';
import { TableBody } from '../TableBody/TableBody';
import { TableCell } from '../TableCell/TableCell';
import { TableFooter } from '../TableFooter/TableFooter';
import { TableHeader } from '../TableHeader/TableHeader';
import { TableRow } from '../TableRow/TableRow';
import { Table } from './Table';

describe(Table, () => {
baselineComponent((props) => (
<Table {...props}>
<TableHeader>
<TableRow>
<TableCell>Header 1</TableCell>
<TableCell>Header 1</TableCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell asHeader scope="row">
Column 1
</TableCell>
<TableCell>Column 2</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell>Footer 1</TableCell>
<TableCell>Footer 2</TableCell>
</TableRow>
</TableFooter>
</Table>
));
});
22 changes: 22 additions & 0 deletions packages/vkui/src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import type { HasRootRef } from '../../types';
import { RootComponent } from '../RootComponent/RootComponent';
import { TableContext } from './TableContext';
import { DEFAULT_TABLE_PADDING } from './constants';
import type { TableContextProps } from './types';
import styles from './Table.module.css';

export interface TableProps
extends TableContextProps,
React.TableHTMLAttributes<HTMLTableElement>,
HasRootRef<HTMLElement> {}

export const Table = ({ padding = DEFAULT_TABLE_PADDING, children, ...restProps }: TableProps) => {
return (
<TableContext.Provider value={{ padding }}>
<RootComponent Component="table" baseClassName={styles.Table} {...restProps}>
{children}
</RootComponent>
</TableContext.Provider>
);
};
11 changes: 11 additions & 0 deletions packages/vkui/src/components/Table/TableContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from 'react';
import { DEFAULT_TABLE_PADDING } from './constants';
import type { TableContextProps, TableSectionContextProps } from './types';

export const TableContext = React.createContext<TableContextProps>({
padding: DEFAULT_TABLE_PADDING,
});

export const TableSectionContext = React.createContext<TableSectionContextProps | undefined>(
undefined,
);
1 change: 1 addition & 0 deletions packages/vkui/src/components/Table/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_TABLE_PADDING = 'm';
20 changes: 20 additions & 0 deletions packages/vkui/src/components/Table/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { LiteralUnion } from '../../types';

export type TableContextProps = {
padding?: LiteralUnion<'xs' | 's' | 'm' | 'l', number | string>;
};

export type TableSectionType = 'header' | 'body' | 'footer';

export type TableSectionContextProps =
| {
type: 'header';
isSticky: boolean;
}
| {
type: 'body';
}
| {
type: 'footer';
isSticky: boolean;
};
Empty file.
14 changes: 14 additions & 0 deletions packages/vkui/src/components/TableBody/TableBody.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// import * as React from 'react';
import type { Meta } from '@storybook/react';
import { withVKUILayout } from '../../storybook/VKUIDecorators';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { TableBody, type TableBodyProps } from './TableBody';

const story: Meta<TableBodyProps> = {
title: 'Layout/TableBody',
component: TableBody,
parameters: { ...CanvasFullLayout, ...DisableCartesianParam },
decorators: [withVKUILayout],
};

export default story;
Loading

0 comments on commit 4653c78

Please sign in to comment.