From 7e21249772dd91bd598afbac50fa680758709d22 Mon Sep 17 00:00:00 2001 From: tatinacher Date: Mon, 9 Aug 2021 18:19:05 +0300 Subject: [PATCH] [FRNT-507] Implement data table (#124) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: implement data-table * feat(data-table): add head renderer * feat(data-table): add rowKey * refactor(data-table): split up things * fix(data-table/filter): checkboxes behavior * refactor(data-table): small changes * fix(data-table): types * fix(data-table): import * feat(data-table): add an export for DataTableColumn type * fix(data-table): usage and types * refactor(data-table): imports Co-authored-by: Ainur <33234903+purplecandle@users.noreply.github.com> * style(data-table): format * refactor(data-table): remove unnecessary variable for placeholder * fix(data-table): replace variant with priority * [FRNT-548] Add documentation configurator (#170) * feat(docs): add configurators (+ color configurator) * style(docs): fix spaces * style(icons): format * style: lint styles * chore: add lint:style:fix command * perf(configurators): replace heavy libraries * [FRNT-562] add icon component (#163) * add icon component * fix * fix naming * fix usage * fix usage * [FRNT-559] add toast weight (#165) * add toast weight * fix chip and button usage * fix toast styles * [FRNT-564] Implement color palette fabrics (#162) * feat: implement color palette fabrucs * refactor: remame variable Co-authored-by: Шараев Айнур Раильевич * [FRNT-568] add font stacks (#153) * feat: add system-ui as a fallback font, add a font-weight vars * feat: move font-stacks to a separate file * refactor: remove font-weight, rename system font-stack * refactor: rebase and format * [FRNT-560] add weight to button-icon (#164) * add weight to button-icon * add goast styles * fix icon-button styles * [FRNT-396] Rewrite data selectors (#150) * fix: rewrite data-selector, fix grammar * feat: write styleguide for data-attribute * update Styleguide Co-authored-by: Ira * [FRNT-520] Rewrite tooltip (#129) * chore: bump version (#169) * feat(docs): add configurators (+ color configurator) * style(docs): fix spaces * feat(docs): add white priority to configurators * refactor(configurators): change layout and color * chore(deps): update lock * fix(configurators): hardcoded configurator name * refactor(configurators): remove unused data-attribute * refactor(configurators): replace transparency parrent with svg * refactor(configurators): styling * style(configurators): remove unnecessary xmlns attribute * refactor(configurators): styling * style(configurators): remove unnecessary variables * style(configurators): remove useless code * refactor(configurator): stylesheets, simplify and add some comments * fix(configurators): add viewbox for svg * refactor(configurators): remove unused refs * feat(configurators): add scope colors * feat(configurators): limit the height of configurator's tab * refactor(configurators): remove unused forwardRef * fix(configurators): typo Co-authored-by: Ainur <33234903+purplecandle@users.noreply.github.com> * refactor(configurators): simplify toggle state * chore(deps): sync lock * refactor(configurators): size units * fix(configurators): remove tertiary priority, add success priority Co-authored-by: Irina Aristova <38761239+Irinaristova@users.noreply.github.com> Co-authored-by: tatinacher Co-authored-by: Шараев Айнур Раильевич Co-authored-by: Ainur <33234903+purplecandle@users.noreply.github.com> Co-authored-by: Ira Co-authored-by: rchubarkin * [FRNT-603] Refactor data-attributes (#181) * style: change styling data-attributes * docs(styleguide): update information about data-attributes * feat: move all dev stuff from lib to dev dir (#182) * feat(docs): upgrade gatsby theme version (#183) * [FRNT-592] feat: implement level decrement (#179) * feat: implement level decrement task: FRNT-592 * fix(LevelDecrement): style inner component instead of wrapper * fix(LevelDecrement): naming * feat: implement level downgrade * fix: more specific exported global styles name * fix(lib): level downgrade better naming * [FRNT-543] create tab and tabbar component (#123) * create tab ana tabbar component * fix lint styles * fix tabs styles * fix tab styles * fix shadow * fix styles * rewtire styles * update tabs styles * fix styles * update styles * add weight to tab * update tab styles * fix styles * fix styles * [DS-40] Fix refactored data-icon in box components (#186) * feat(data-table): improve usage * fix(table): text color * fix(data-table): typo Co-authored-by: Ainur <33234903+purplecandle@users.noreply.github.com> * fix(table): add comment Co-authored-by: risenforces Co-authored-by: Ainur <33234903+purplecandle@users.noreply.github.com> Co-authored-by: Irina Aristova <38761239+Irinaristova@users.noreply.github.com> Co-authored-by: Шараев Айнур Раильевич Co-authored-by: Ira Co-authored-by: rchubarkin --- package.json | 2 + src/woly/atoms/table/index.tsx | 9 +- src/woly/atoms/table/usage.mdx | 10 +- src/woly/molecules/data-table/filter.tsx | 116 +++++++++++ src/woly/molecules/data-table/index.tsx | 71 +++++++ src/woly/molecules/data-table/range-cell.tsx | 15 ++ src/woly/molecules/data-table/types.ts | 39 ++++ src/woly/molecules/data-table/usage.mdx | 200 +++++++++++++++++++ src/woly/molecules/index.ts | 1 + yarn.lock | 30 ++- 10 files changed, 484 insertions(+), 9 deletions(-) create mode 100644 src/woly/molecules/data-table/filter.tsx create mode 100644 src/woly/molecules/data-table/index.tsx create mode 100644 src/woly/molecules/data-table/range-cell.tsx create mode 100644 src/woly/molecules/data-table/types.ts create mode 100644 src/woly/molecules/data-table/usage.mdx diff --git a/package.json b/package.json index b582e9fd..ea38c7da 100755 --- a/package.json +++ b/package.json @@ -108,6 +108,8 @@ "dependencies": { "@tippyjs/react": "^4.2.5", "color-rgba": "^2.2.3", + "effector": "^21.8.12", + "effector-react": "^21.3.3", "nanoid": "^3.1.23", "react": "16.14.0", "react-colorful": "^5.2.3", diff --git a/src/woly/atoms/table/index.tsx b/src/woly/atoms/table/index.tsx index bd38a4e8..6624e50a 100644 --- a/src/woly/atoms/table/index.tsx +++ b/src/woly/atoms/table/index.tsx @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled, { StyledComponent } from 'styled-components'; import { Priority } from 'lib/types'; const map = (properties: { columns: number } & Priority) => ({ @@ -17,7 +17,7 @@ export const Table = styled.table.attrs(map)` display: grid; grid-template-columns: repeat(var(--local-columns), auto); gap: var(--local-gap); -`; +` as StyledComponent<'table', Record, { columns: number } & Priority>; export const Thead = styled.thead` display: contents; @@ -32,9 +32,13 @@ export const Th = styled.th` align-items: center; box-sizing: border-box; max-width: var(--local-cell-max-width); + /* TODO: Replace with box [09.08.2020] */ padding: var(--local-vertical) var(--local-horizontal); color: var(--woly-canvas-text-disabled); + font-weight: normal; + + line-height: var(--woly-line-height); background: var(--woly-shape-text-default); `; @@ -46,6 +50,7 @@ export const Td = styled.td` padding: var(--local-vertical) var(--local-horizontal); color: var(--woly-canvas-text-default); + line-height: var(--woly-line-height); background: var(--woly-shape-text-default); `; diff --git a/src/woly/atoms/table/usage.mdx b/src/woly/atoms/table/usage.mdx index 5caf6a7a..7805d267 100644 --- a/src/woly/atoms/table/usage.mdx +++ b/src/woly/atoms/table/usage.mdx @@ -1,6 +1,11 @@ import {Playground} from 'dev/playground' import {Table, Tbody, Td, Th, Thead, Tr} from 'ui' +To create a table use Table, Thead, Tbody, Th, Tr, Td components. +To set number of columns pass `columns` prop to the Table component. + +### Example + export const tableHead = [ {id: 'id', name: 'Id'}, {id: 'firstName', name: 'First name'}, @@ -65,11 +70,6 @@ export const tableRows = [ }, ]; -To create a table use Table, Thead, Tbody, Th, Tr, Td components. -To set number of columns pass `columns` prop to the Table component. - -### Example - diff --git a/src/woly/molecules/data-table/filter.tsx b/src/woly/molecules/data-table/filter.tsx new file mode 100644 index 00000000..2667779d --- /dev/null +++ b/src/woly/molecules/data-table/filter.tsx @@ -0,0 +1,116 @@ +import * as React from 'react'; +import styled from 'styled-components'; +import { IconArrowDown } from 'static/icons'; +import { box } from 'ui/elements/box'; + +import { Button, Checkbox, ListContainer, Popover, Surface } from '../../index'; + +interface Option { + name: string; + value: string; +} + +interface FilterProps { + title: React.ReactNode | string; + options: Option[]; + value: string[]; + onChange: (value: string[]) => void; +} + +export const Filter: React.FC = ({ + title, + options, + value: checkedValues, + onChange, +}) => { + const [isOpen, setOpen] = React.useReducer((is) => !is, false); + + const getUpdatedValue = (value: string) => { + if (checkedValues.includes(value)) { + // remove + return checkedValues.filter((item) => item !== value); + } + + // add + return checkedValues.concat(value); + }; + + const createCheckHandler = (item: string) => { + return () => onChange(getUpdatedValue(item)); + }; + + if (options.length === 0) { + console.log('No options are passed to filter'); + return null; + } + + return ( + + + + {options.map(({ name, value }) => ( + + ))} + + + } + > + +
{title}
+
+ +
+
+
+
+ ); +}; + +const Dropdown = styled(Surface)` + position: absolute; + + display: flex; + flex-direction: column; + width: 100%; + + font-weight: normal; +`; + +const FilterBlock = styled.div` + position: relative; +`; + +const FilterButton = styled.div` + ${box} + + display: flex; + align-items: center; + box-sizing: border-box; + + background: var(--woly-shape-text-default); + + border: var(--woly-border-width) solid var(--woly-canvas-default); + + cursor: pointer; + + [data-icon] { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + svg > path { + fill: var(--woly-canvas-text-disabled); + } + } +`; diff --git a/src/woly/molecules/data-table/index.tsx b/src/woly/molecules/data-table/index.tsx new file mode 100644 index 00000000..cddcb3fc --- /dev/null +++ b/src/woly/molecules/data-table/index.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; + +import { CellProps, DataTableProps, HeadGroupProps, HeadProps } from './types'; +import { Table, Tbody, Td, Th, Thead, Tr } from '../../atoms/table'; + +export function DataTable({ + rowKey, + columns, + placeholder = '----', + priority = 'secondary', + values, + ...rest +}: DataTableProps) { + return ( + // eslint-disable-next-line react/jsx-props-no-spreading +
+ + + {values.map((row) => ( + + {columns.map((column) => { + const Cell = column.cell || DefaultCell; + + return ( + + ); + })} + + ))} + +
+ +
+ ); +} + +function DefaultCell({ value, placeholder }: CellProps) { + return <>{value || placeholder}; +} + +function DefaultHead({ title }: HeadProps) { + return <>{title}; +} + +function TableHeadGroup({ columns }: HeadGroupProps) { + return ( + + + {columns.map(({ title, property, head }) => { + const Head = head || DefaultHead; + const padding = isReactEntity(title) ? '0' : ''; + + return ( + + + + ); + })} + + + ); +} + +const isReactComponent = (value: unknown) => typeof value === 'function'; +const isReactElement = (value: unknown) => typeof value === 'object'; +const isReactEntity = (value: unknown) => isReactComponent(value) || isReactElement(value); + +export { DataTableColumn, HeadProps as DataTableHeadProps } from './types'; diff --git a/src/woly/molecules/data-table/range-cell.tsx b/src/woly/molecules/data-table/range-cell.tsx new file mode 100644 index 00000000..50582ed8 --- /dev/null +++ b/src/woly/molecules/data-table/range-cell.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +interface RangeProps { + value: { + from: number; + to: number; + }; + placeholder: string; +} + +export const RangeCell: React.FC = ({ value, placeholder }) => ( + + from {value.from || placeholder} to {value.to || placeholder} + +); diff --git a/src/woly/molecules/data-table/types.ts b/src/woly/molecules/data-table/types.ts new file mode 100644 index 00000000..80c8f339 --- /dev/null +++ b/src/woly/molecules/data-table/types.ts @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { Priority } from 'lib/types'; + +export type CellType = { + [key in TRowKey]: string; +} & + Record; + +export interface HeadProps { + title: React.ReactNode | string; +} + +export interface CellProps { + placeholder?: React.ReactNode | string; + value: TValue; +} + +export interface DataTableColumn { + title: React.ReactNode | string; + property: string; + head?: React.FC; + cell?: React.FC>; + placeholder?: React.ReactNode | string; +} + +export type DataTableProps< + TValue, + TRowKey extends string +> = React.HTMLAttributes & + Priority & { + rowKey: TRowKey; + columns: Array>; + placeholder?: React.ReactNode | string; + values: Array>; + }; + +export interface HeadGroupProps { + columns: Array>; +} diff --git a/src/woly/molecules/data-table/usage.mdx b/src/woly/molecules/data-table/usage.mdx new file mode 100644 index 00000000..7b04ff10 --- /dev/null +++ b/src/woly/molecules/data-table/usage.mdx @@ -0,0 +1,200 @@ +### Example + +```tsx playground +import { DataTable, DataTableHeadProps } from 'woly' +import { createStore, createEvent, forward, combine } from 'effector' +import { useStore } from 'effector-react' +import { Filter } from './filter' +import { RangeCell } from './range-cell' + +const bankNames = [ + { + value: 'ВТБ', + name: 'ВТБ', + }, + { + value: 'Тинькофф', + name: 'Тинькофф', + }, + { + value: 'Альфа-банк', + name: 'Альфа', + }, + { + value: 'Открытие', + name: 'Открытие', + }, +]; + +const paymentSystems = [ + { + value: 'Visa/Mastercard', + name: 'Visa/Mastercard', + }, + { + value: 'МИР', + name: 'МИР', + }, +]; + +const $bankFilter = createStore([]) +const bankFilterUpdated = createEvent() +forward({ from: bankFilterUpdated, to: $bankFilter }) + +const $paymentSystemFilter = createStore([]) +const paymentSystemFilterUpdated = createEvent() +forward({ from: paymentSystemFilterUpdated, to: $paymentSystemFilter }) + +const columns = [ + { + title: 'ID Дебет', + property: 'id-debet', + }, + { + title: 'ID Кредит', + property: 'id-credit', + }, + { + title: 'Название банка', + property: 'bank-name', + head: ({ title }: DataTableHeadProps) => ( + + ), + }, + { + title: 'Платежная система', + property: 'payment-system', + head: ({ title }: DataTableHeadProps) => ( + + ), + }, + { + title: 'Диапазон', + property: 'range', + cell: RangeCell, + }, +]; + +const values = [ + { + id: 1, + 'id-debet': 798172, + 'id-credit': null, + 'bank-name': 'ВТБ', + 'payment-system': 'Visa/Mastercard', + range: { + from: 1, + to: 4, + }, + }, + { + id: 2, + 'id-debet': 798173, + 'id-credit': null, + 'bank-name': 'Альфа-банк', + 'payment-system': 'Visa/Mastercard', + range: { + from: 4, + to: 6, + }, + }, + { + id: 3, + 'id-debet': 798174, + 'id-credit': null, + 'bank-name': 'ВТБ', + 'payment-system': 'МИР', + range: { + from: 4, + to: 6, + }, + }, + { + id: 4, + 'id-debet': 798175, + 'id-credit': null, + 'bank-name': 'Тинькофф', + 'payment-system': 'Visa/Mastercard', + range: { + from: 3, + to: 6, + }, + }, + { + id: 5, + 'id-debet': 798176, + 'id-credit': null, + 'bank-name': 'Открытие', + 'payment-system': 'МИР', + range: { + from: 4, + to: 6, + }, + }, + { + id: 6, + 'id-debet': 798177, + 'id-credit': null, + 'bank-name': 'Альфа-банк', + 'payment-system': 'МИР', + range: { + from: 4, + to: 9, + }, + }, +]; + +const $filteredValues = combine( + $bankFilter, + $paymentSystemFilter, + (banks, paymentSystems) => { + return values.filter(value => { + const bankMatches = banks.length === 0 || banks.includes(value['bank-name']) + const paymentSystemMatches = paymentSystems.length === 0 || paymentSystems.includes(value['payment-system']) + console.log(banks, value['bank-name']) + return bankMatches && paymentSystemMatches + }) + } +) + +export function Example() { + const values = useStore($filteredValues) + + return + + + + +} +``` + +### Components + +| Name | Description | +| ----------- | ------------------- | +| `DataTable` | DataTable component | + +### DataTable Props + +| Name | Type | Default | Description | +| ------------- | ---------------------------------- | ------------- | -------------------------------------------- | +| `columns` | `Array` | | Table head | +| `placeholder` | `React.ReactNode ӏ string` | `'----'` | String that is displayed when value is empty | +| `values` | `Array>` | | Table content | +| `priority` | `string` | `'secondary'` | Priority prop to style DataTable component | + +### HeadProps + +| Name | Type | Default | Description | +| ------- | -------------------------- | ------- | ------------------------------------------------- | +| `title` | `React.ReactNode ӏ string` | | A column name passed via `title` of `ColumnProps` | + +### ColumnProps + +| Name | Type | Default | Description | +| ------------- | -------------------------- | ------- | ------------------------------------------------------------- | +| `title` | `React.ReactNode ӏ string` | | A column name. Can be a simple string or a react node | +| `property` | `string` | | Unique property of column to connect head with value in a row | +| `head` | `React.FC` | | FC to render head | +| `cell` | `React.FC` | | FC to render cell | +| `placeholder` | `React.ReactNode ӏ string` | | A column-specific placeholder | diff --git a/src/woly/molecules/index.ts b/src/woly/molecules/index.ts index 853933fa..aa986f5d 100644 --- a/src/woly/molecules/index.ts +++ b/src/woly/molecules/index.ts @@ -1,5 +1,6 @@ export { Accordion } from './accordion'; export { Checkbox } from './checkbox'; +export { DataTable, DataTableColumn, HeadProps as DataTableHeadProps } from './data-table'; export { Field } from './field'; export { InputPassword } from './input-password'; export { Notification } from './notification'; diff --git a/yarn.lock b/yarn.lock index e4984e92..778d573f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7112,6 +7112,16 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +effector-react@^21.3.3: + version "21.3.3" + resolved "https://registry.yarnpkg.com/effector-react/-/effector-react-21.3.3.tgz#e17c095ca7b6d63af3528b1b5ebf94c3de0149db" + integrity sha512-pk+eZQcoI0q8sMtxAvm/lWsZutHW5t0aQBt0edy1XTvjKIhgimFhpxUqJtdtr0asz+9OCN0iG6FYa+Iqo6qpkw== + +effector@^21.8.12: + version "21.8.12" + resolved "https://registry.yarnpkg.com/effector/-/effector-21.8.12.tgz#075ba0e17e41386bef271567a259585f407067ae" + integrity sha512-Xlw+qvPlfiF0qOHwRQ3RzS5/TtbfpCZ88ucLqG6o8algEV0nxI+Rd8fyJObDljfqQH0+2ByInawu731IXz5RCA== + electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.649: version "1.3.752" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09" @@ -7423,7 +7433,7 @@ eslint-import-resolver-alias@^1.1.2: resolved "https://registry.yarnpkg.com/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz#297062890e31e4d6651eb5eba9534e1f6e68fc97" integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w== -eslint-import-resolver-node@^0.3.3, eslint-import-resolver-node@^0.3.4: +eslint-import-resolver-node@^0.3.3: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== @@ -7431,6 +7441,14 @@ eslint-import-resolver-node@^0.3.3, eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" +eslint-import-resolver-node@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.5.tgz#939bbb0f74e179e757ca87f7a4a890dabed18ac4" + integrity sha512-XMoPKjSpXbkeJ7ZZ9icLnJMTY5Mc1kZbCakHquaFsXPpyWOwK0TK6CODO+0ca54UoM9LKOxyUNnoVZRl8TeaAg== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + eslint-import-resolver-typescript@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.3.0.tgz#0870988098bc6c6419c87705e6b42bee89425445" @@ -7442,7 +7460,7 @@ eslint-import-resolver-typescript@2.3.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-module-utils@^2.6.0, eslint-module-utils@^2.6.1: +eslint-module-utils@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== @@ -7450,6 +7468,14 @@ eslint-module-utils@^2.6.0, eslint-module-utils@^2.6.1: debug "^3.2.7" pkg-dir "^2.0.0" +eslint-module-utils@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz#94e5540dd15fe1522e8ffa3ec8db3b7fa7e7a534" + integrity sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q== + dependencies: + debug "^3.2.7" + pkg-dir "^2.0.0" + eslint-plugin-flowtype@^5.3.1: version "5.7.2" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz#482a42fe5d15ee614652ed256d37543d584d7bc0"