From 921a1a15ef8266dcb4b2cc06b2a713f48b5792e1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2020 13:09:02 +0000 Subject: [PATCH 1/7] chore(deps): bump @types/react-datepicker from 2.11.1 to 3.0.0 Bumps [@types/react-datepicker](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-datepicker) from 2.11.1 to 3.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-datepicker) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c56d32b..cc146664 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "@fullcalendar/timegrid": "~4.4.2", "@tinymce/tinymce-react": "~3.6.0", "@types/react-bootstrap-typeahead": "^3.4.6", - "@types/react-datepicker": "~2.11.0", + "@types/react-datepicker": "~3.0.0", "babel-jest": "~26.0.1", "chart.js": "~2.9.3", "date-fns": "~2.9.0", From 7ff56485ed7a67af9e7c1808c49a28cc0e02a21e Mon Sep 17 00:00:00 2001 From: Kumiko Date: Wed, 17 Jun 2020 20:30:32 -0700 Subject: [PATCH 2/7] feat(table): update table (#455) Co-authored-by: Matteo Vivona --- src/components/Table/Table.tsx | 210 +++------ src/components/Table/helper.tsx | 216 --------- src/components/Table/index.tsx | 1 - src/components/Table/interfaces.ts | 55 --- stories/table.stories.tsx | 183 +++---- test/table.test.tsx | 734 ++++------------------------- 6 files changed, 235 insertions(+), 1164 deletions(-) delete mode 100644 src/components/Table/helper.tsx delete mode 100644 src/components/Table/interfaces.ts diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 31f33fb9..6e1e79d2 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -1,151 +1,89 @@ import React from 'react' -import { useTable, useFilters, useSortBy, usePagination, TableInstance } from 'react-table' +import { ButtonVariant } from 'src/interfaces' -import { generateColumns } from './helper' -import { Data, TableProperties, GeneratedColumn } from './interfaces' +import { Button } from '../Button' + +type T = { [key: string]: any } interface Props { - /** TableProperties are composed by a tableClassname string property and the columns array */ - tableProperties: TableProperties - /** Provides data for the table */ - data?: Data[] + tableClassName: string + headerClassName: string + columns: { key: string; label: string; formatter?: (row: T) => React.ReactNode }[] + data: T[] + actionsHeaderText: string + actions?: { label: string; action: (row: T) => void; buttonColor?: ButtonVariant }[] + getID: (row: T) => string + onRowClick?: (row: T) => void } -function Table({ data, tableProperties }: Props) { - const columns = React.useMemo(() => generateColumns(tableProperties.columns), []) - +const Table = (props: Props) => { const { - getTableProps, - getTableBodyProps, - headerGroups, - page, - prepareRow, - canPreviousPage, - canNextPage, - pageOptions, - pageCount, - gotoPage, - nextPage, - previousPage, - setPageSize, - state: { pageIndex, pageSize }, - } = useTable( - { - columns, - data: data || [], - initialState: { pageIndex: 0 }, - }, - useFilters, - useSortBy, - usePagination, - ) as TableInstance> - - return ( - <> - - - {headerGroups.map((headerGroup) => ( - - {headerGroup.headers.map((column: GeneratedColumn) => ( - - ))} - + const table = ( +
-
- {column.render('Header')} - {column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''} -
+ tableClassName, + headerClassName, + columns, + data, + actionsHeaderText, + actions, + getID, + onRowClick, + } = props -
{!column.disableFiltering && column.render('Filter')}
-
+ + + {columns.map((column) => ( + ))} - - - {page.map((row) => { - prepareRow(row) - return ( - - {row.cells.map((cell: any) => ( - + + + + {data.map((row: T) => ( + { + if (onRowClick) { + onRowClick(row) + } + }} + > + {columns.map((column) => { + const content = !column.formatter ? row[column.key] : column.formatter(row) + return + })} + + {actions ? ( + + {label} + ))} - - ) - })} - -
{column.label}
{actionsHeaderText} : null} +
{content} + {actions.map(({ label, action, buttonColor }, i) => ( +
-
- {' '} - {' '} - {' '} - {' '} - - Page{' '} - - {data && data.length ? pageIndex + 1 : pageIndex} of {pageOptions.length} - {' '} - - - | Go to page:{' '} - { - const p = e.target.value ? Number(e.target.value) - 1 : 0 - gotoPage(p) - }} - style={{ width: '100px' }} - /> - {' '} - -
- + + ) : null} + + ))} + + ) + + return table +} + +Table.defaultProps = { + tableClassName: 'table table-hover', + headerClassName: 'thead-light', + actionsHeaderText: 'Actions', } export { Table } diff --git a/src/components/Table/helper.tsx b/src/components/Table/helper.tsx deleted file mode 100644 index 859d4ffa..00000000 --- a/src/components/Table/helper.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import { get } from 'lodash' -import React from 'react' -import { CellProps } from 'react-table' - -import { DateTimePicker } from '../DateTimePicker' -import { CustomColumn, ColumnStyle, DefaultColumnFilterOptions, Element } from './interfaces' - -const DefaultColumnFilter = ({ - column: { filterValue = '', setFilter, filterPlaceholder = '' }, -}: DefaultColumnFilterOptions) => ( - { - setFilter(e.target.value || undefined) - }} - placeholder={filterPlaceholder || 'Search records...'} - /> -) - -const SelectColumnFilter = ({ - column: { filterValue, setFilter, preFilteredRows, id = '0' }, -}: DefaultColumnFilterOptions) => { - const options = React.useMemo(() => { - const opt: string[] = [] - preFilteredRows.forEach((row) => { - if (opt.indexOf(row.values[id]) === -1) { - opt.push(row.values[id]) - } - }) - return [...opt] - }, [id, preFilteredRows]) - - return ( - - ) -} - -const DateColumnFilter = ({ - column: { filterValue = '', setFilter }, -}: DefaultColumnFilterOptions) => ( -
- { - setFilter(date.toString()) - }} - selected={filterValue ? new Date(filterValue) : new Date()} - timeIntervals={30} - withPortal={false} - /> -
-) - -export const generateColumns = (columns: CustomColumn[]) => { - const generatedColumns: Element[] = [] - let el: Element - - columns.map((element) => { - switch (get(element, 'type', '')) { - case 'boolean': - el = { - Header: element.title || element.accessor || '', - accessor: element.accessor || '', - Cell: - element.undefinedMeansFalse === false - ? element.customTrueIcon - ? element.customFalseIcon - ? ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && element.customTrueIcon} - {row.values[element.accessor] === false && element.customFalseIcon} - {row.values[element.accessor] !== true && - row.values[element.accessor] !== false &&
} - - ) - : ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && element.customTrueIcon} - {row.values[element.accessor] === false && ( -
false
- )} - {row.values[element.accessor] !== true && - row.values[element.accessor] !== false &&
} - - ) - : element.customFalseIcon - ? ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && ( -
true
- )} - {row.values[element.accessor] === false && element.customFalseIcon} - {row.values[element.accessor] !== true && - row.values[element.accessor] !== false &&
} - - ) - : ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && ( -
true
- )} - {row.values[element.accessor] === false && ( -
false
- )} - {row.values[element.accessor] !== true && - row.values[element.accessor] !== false &&
} - - ) - : element.customTrueIcon - ? element.customFalseIcon - ? ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && element.customTrueIcon} - {row.values[element.accessor] !== true && element.customFalseIcon} - - ) - : ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && element.customTrueIcon} - {row.values[element.accessor] !== true && ( -
false
- )} - - ) - : element.customFalseIcon - ? ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && ( -
true
- )} - {row.values[element.accessor] !== true && element.customFalseIcon} - - ) - : ({ row }: CellProps>) => ( - <> - {row.values[element.accessor] === true && ( -
true
- )} - {row.values[element.accessor] !== true && ( -
false
- )} - - ), - className: element.className || '', - headerClassName: element.headerClassName || '', - disableSorting: element.disableSorting, - disableFiltering: element.disableFiltering, - Filter: SelectColumnFilter, - } - generatedColumns.push(el) - return generatedColumns - case 'date': - el = { - Header: element.title || element.accessor || '', - accessor: element.accessor || '', - className: element.className ? `${element.className} text-center` : 'text-center', - headerClassName: element.headerClassName || '', - disableSorting: element.disableSorting, - disableFiltering: element.disableFiltering, - filterPlaceholder: element.filterPlaceholder || '', - Filter: DateColumnFilter, - } - generatedColumns.push(el) - return generatedColumns - default: - el = { - Header: element.title || element.accessor || '', - accessor: element.accessor || '', - Cell: ({ row }: CellProps>) => { - let resultingStyle = {} - let applyStyle = true - if (element.styles && element.styles.length) { - element.styles.forEach((columnsStyle: ColumnStyle) => { - if (columnsStyle.conditions) { - // TODO check conditions and eventually toggle applyStyle - applyStyle = true - } - if (applyStyle) { - resultingStyle = { ...resultingStyle, ...columnsStyle.style } - } - }) - } - return resultingStyle ? ( -
{row.values[element.accessor]}
- ) : ( - <>{row.values[element.accessor]} - ) - }, - className: element.className || '', - headerClassName: element.headerClassName || '', - Filter: DefaultColumnFilter, - disableSorting: element.disableSorting, - disableFiltering: element.disableFiltering, - filterPlaceholder: element.filterPlaceholder || '', - } - generatedColumns.push(el) - return generatedColumns - } - }) - return generatedColumns -} diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 2fec913e..a86149cb 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -1,2 +1 @@ export * from './Table' -export * from './interfaces' diff --git a/src/components/Table/interfaces.ts b/src/components/Table/interfaces.ts deleted file mode 100644 index 348e7439..00000000 --- a/src/components/Table/interfaces.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ColumnInstance, UseSortByColumnProps, UseFiltersColumnProps } from 'react-table' - -export interface TableProperties { - tableClassname: string - columns: CustomColumn[] -} - -export interface CustomColumn { - accessor: string - type: 'string' | 'boolean' | 'date' - title: string - label?: string - filterPlaceholder?: string - headerClassName?: string - className?: string - disableFiltering?: boolean - disableSorting?: boolean - styles?: ColumnStyle[] - customTrueIcon?: JSX.Element - customFalseIcon?: JSX.Element - undefinedMeansFalse?: boolean -} - -export interface ColumnStyle { - conditions?: any - style: StyleObject -} - -type StyleObject = Record - -export interface DefaultColumnFilterOptions { - column: TableColumn> -} - -interface CustomFilters { - filterPlaceholder?: string -} - -interface TableColumn = Record> - extends ColumnInstance, - UseSortByColumnProps, - UseFiltersColumnProps, - CustomFilters {} - -export type Element = any - -export type Data = Record - -interface CustomSubColumn { - headerClassName?: string - disableFiltering?: boolean - disableSorting?: boolean -} - -export interface GeneratedColumn extends CustomSubColumn, Omit {} diff --git a/stories/table.stories.tsx b/stories/table.stories.tsx index a17a4f93..00d2c177 100644 --- a/stories/table.stories.tsx +++ b/stories/table.stories.tsx @@ -1,7 +1,8 @@ import { storiesOf } from '@storybook/react' import React from 'react' -import { Table, Icon, TableProperties } from '../src' +import { Table, Toast, Toaster } from '../src' +import { ButtonVariant } from '../src/interfaces' storiesOf('Table', module) .addParameters({ @@ -10,144 +11,78 @@ storiesOf('Table', module) }, }) .addDecorator((storyFn) => ( -
{storyFn()}
+
{storyFn()}
)) .add('Demo Table', () => { + const ID = 'id' + const NAME = 'name' + const PHONE = 'phone' + const DRINKS = 'drinks' + + const getDrinksList = (row: any) => ( +
    + {row[DRINKS].map((d: string) => ( +
  • {d}
  • + ))} +
+ ) + + const columns = [ + { key: ID, label: 'ID' }, + { key: NAME, label: 'Name' }, + { key: PHONE, label: 'Phone #' }, + { key: DRINKS, label: 'Drinks?', formatter: getDrinksList }, + ] + const data = [ { - firstName: 'John', - lastName: 'Smith', - age: 20, - status: 'married', - admin: true, - date: new Date().toString(), + [ID]: 333, + [NAME]: 'Mickey', + [PHONE]: '789-777', + [DRINKS]: ['milk', 'tea'], }, { - firstName: 'Jack', - lastName: 'Doe', - age: 21, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Jason', - lastName: 'Gutemberg', - age: 22, - status: 'married', - admin: false, - date: new Date().toString(), + [ID]: 777, + [NAME]: 'Minnie', + [PHONE]: '333-123', + [DRINKS]: ['water', 'coffee'], }, ] - const tableProperties: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Name', - headerClassName: 'text-info', - className: 'pl-3 fixoverflow', - disableFiltering: false, - disableSorting: false, - filterPlaceholder: 'Search in names!', - styles: [ - { - conditions: [ - { - or: [ - { - and: [ - { - or: [ - { - property: 'firstName', - value: 'John', - }, - { - property: 'firstName', - value: 'Jack', - }, - ], - }, - { - property: 'firstName', - value: 'Mob', - }, - ], - }, - { - and: [ - { - property: 'firstName', - value: 'Fog', - }, - ], - }, - ], - }, - ], - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'lastName', - type: 'string', - title: 'Surname', - headerClassName: 'text-info', - className: 'pl-3 fixoverflow', - disableFiltering: false, - disableSorting: true, - }, - { - accessor: 'age', - type: 'string', - title: 'Age', - headerClassName: '', - className: 'pl-3 fixoverflow', - disableFiltering: false, - disableSorting: false, - filterPlaceholder: 'Search in ages', - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - headerClassName: 'text-info', - className: 'pl-3 fixoverflow', - disableFiltering: false, - disableSorting: false, - customTrueIcon: ( -
- -
- ), - customFalseIcon: ( -
- -
- ), + const actions = [ + { + label: 'Edit', + action: (row: any) => { + Toast('info', 'an edit clicked', `will edit ID=${row[ID]}`) }, - { - accessor: 'date', - type: 'date', - title: 'Date', - headerClassName: '', - className: 'pl-3 fixoverflow', - disableFiltering: false, - disableSorting: false, - filterPlaceholder: 'Search in dates', + buttonColor: 'info' as ButtonVariant, + }, + { + label: 'Delete', + action: (row: any) => { + Toast('error', 'a delete clicked', `will delete ID=${row[ID]}`) }, - ], + buttonColor: 'danger' as ButtonVariant, + }, + ] + + const getID = (row: any) => row[ID] + + const onRowClick = (row: any) => { + Toast('success', 'a row clicked', `${row[NAME]} @ ${row[PHONE]}`) } return (
- +
+ + ) }) diff --git a/test/table.test.tsx b/test/table.test.tsx index e433b079..03ab355c 100644 --- a/test/table.test.tsx +++ b/test/table.test.tsx @@ -1,669 +1,139 @@ import { mount } from 'enzyme' import * as React from 'react' +import { Button } from 'react-bootstrap' + +import { Table } from '../src' + +// setup +const ID = 'id' +const NAME = 'name' +const PHONE = 'phone' +const DRINKS = 'drinks' + +const getDrinksList = (row: any) => ( +
    + {row[DRINKS].map((d: string) => ( +
  • {d}
  • + ))} +
+) + +const columns = [ + { key: ID, label: 'ID' }, + { key: NAME, label: 'Name' }, + { key: PHONE, label: 'Phone #' }, + { key: DRINKS, label: 'Drinks?', formatter: getDrinksList }, +] -import { Table, Icon } from '../src' -import { TableProperties } from '../src/components/Table/interfaces' - -const shortData = [ +const data = [ { - firstName: 'John', - lastName: 'Smith', - age: 20, - status: 'married', - admin: true, - date: new Date().toString(), + [ID]: 333, + [NAME]: 'Mickey', + [PHONE]: '789-777', + [DRINKS]: ['milk', 'tea'], }, { - firstName: 'Jack', - lastName: 'Doe', - age: 21, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Jason', - lastName: 'Gutemberg', - age: 22, - status: 'married', - admin: true, - date: new Date().toString(), + [ID]: 777, + [NAME]: 'Minnie', + [PHONE]: '333-123', + [DRINKS]: ['water', 'coffee'], }, ] -const longData = [ - { - firstName: 'John', - lastName: 'Smith', - age: 20, - status: 'married', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Jack', - lastName: 'Doe', - age: 21, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Jason', - lastName: 'Gutemberg', - age: 22, - status: 'married', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Michael', - lastName: 'Scott', - age: 40, - status: 'single', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Johnny', - lastName: 'Carpenter', - age: 21, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Mariah', - lastName: 'Carey', - age: 22, - status: 'married', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Steven', - lastName: 'Spielberg', - age: 40, - status: 'married', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Marlon', - lastName: 'Brando', - age: 25, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Andreas', - lastName: 'Jungdal', - age: 22, - status: 'single', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Leonardo', - lastName: 'Di Caprio', - age: 40, - status: 'married', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Brad', - lastName: 'Pitt', - age: 42, - status: 'single', - admin: true, - date: new Date().toString(), - }, - { - firstName: 'Spicer', - lastName: 'Lovejoy', - age: 55, - status: 'single', - admin: false, - date: new Date().toString(), - }, - { - firstName: 'Jack', - lastName: 'Dawson', - age: 22, - status: 'single', - admin: true, - date: new Date().toString(), - }, +const onClickAction0Spy = jest.fn() +const onClickAction1Spy = jest.fn() + +const actions = [ { - firstName: 'Rose', - lastName: 'Dewitt', - age: 20, - status: 'married', - admin: false, - date: new Date().toString(), + label: 'action0', + action: onClickAction0Spy, }, { - firstName: 'Caledon', - lastName: 'Hockley', - age: 32, - status: 'married', - admin: true, - date: new Date().toString(), + label: 'action1', + action: onClickAction1Spy, }, ] -const tableProperties: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} +const getID = (row: any) => row[ID] -const customTableProperties: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - customTrueIcon: ( -
- -
- ), - customFalseIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV2: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - customTrueIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV3: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - customFalseIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV4: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - conditions: [ - { - or: [], - }, - ], - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - undefinedMeansFalse: false, - customTrueIcon: ( -
- -
- ), - customFalseIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV5: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - conditions: [ - { - or: [], - }, - ], - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - undefinedMeansFalse: false, - customTrueIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV6: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - conditions: [ - { - or: [], - }, - ], - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - undefinedMeansFalse: false, - customFalseIcon: ( -
- -
- ), - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} - -const customTablePropertiesV7: TableProperties = { - tableClassname: '', - columns: [ - { - accessor: 'firstName', - type: 'string', - title: 'Nome', - className: 'pl-3 fixoverflow', - headerClassName: 'text-info', - styles: [ - { - conditions: [ - { - or: [], - }, - ], - style: { - fontWeight: '600', - }, - }, - ], - }, - { - accessor: 'admin', - type: 'boolean', - title: 'Admin', - className: 'pl-3 fixoverflow', - undefinedMeansFalse: false, - }, - { - accessor: 'date', - type: 'date', - title: 'Date', - }, - ], -} +const onRowClickSpy = jest.fn() describe('Table', () => { - it('Table renders itself without crashing', () => { - const tableWrapper = mount(
) - expect(tableWrapper.find('table')).toBeTruthy() - }) - - it('Data cell is rendered correctly', () => { - const tableWrapper = mount(
) - expect(tableWrapper.containsMatchingElement(
John
)).toBeTruthy() - }) - - it('Pagination section is rendered correctly', () => { - const tableWrapper = mount(
) - expect(tableWrapper.containsMatchingElement(1 of 1)).toBeTruthy() + it('should render itself without crashing', () => { + const tableWrapper = mount(
) + expect(tableWrapper.find('table')).toHaveLength(1) }) - it('Column header classes are applied correctly', () => { - const tableWrapper = mount(
) - const headerWrapper = tableWrapper.find('.text-info') - expect(headerWrapper).toBeTruthy() - }) - - it('Boolean column is rendered in green', () => { - const tableWrapper = mount(
) - expect( - tableWrapper.containsMatchingElement(
true
), - ).toBeTruthy() - }) - - it('Pagination buttons are disabled due to results fitting in one page', () => { - const tableWrapper = mount(
) - const paginationWrapper = tableWrapper.find('.pagination') - const nextPageButton = paginationWrapper.find('#nextPageButton') - expect(nextPageButton.props().disabled).toBe(true) - }) - - it('Column filter is rendered correctly', () => { - const tableWrapper = mount(
) - const headerWrapper = tableWrapper.find('.text-info') - const inputWrapper = headerWrapper.find('input') - expect(inputWrapper).toBeTruthy() - }) + it('should render the header row with provided texts', () => { + const tableWrapper = mount( +
, + ) + const headerCellWrappers = tableWrapper.find('th') - it('Column styles are applied correctly', () => { - const tableWrapper = mount(
) - const cellWrapper = tableWrapper.find('.fixoverflow') - const divWrapper = cellWrapper.find('div').first() - expect(divWrapper.props().style).toMatchObject({ fontWeight: '600' }) + headerCellWrappers.forEach((cell, i) => { + if (i === headerCellWrappers.length - 1) { + expect(cell.text()).toEqual('Actions') + } else { + expect(cell.text()).toEqual(columns[i].label) + } + }) }) - it('DatePicker filter is rendered correctly', () => { - const tableWrapper = mount(
) - const datePickerWrapper = tableWrapper.find('.react-datepicker-wrapper') - const inputWrapper = datePickerWrapper.find('input') - expect(inputWrapper).toBeTruthy() - const prependWrapper = tableWrapper.find('.input-group-prepend') - const prependSpan = prependWrapper.find('span') - expect(prependSpan).toBeTruthy() - }) + it('should render a text cell with a provided text', () => { + const tableWrapper = mount(
) + const bodyWrapper = tableWrapper.find('tbody') + const firstRowSecondColWrapper = bodyWrapper.find('tr').at(0).find('td').at(1) - it('Pagination buttons are enabled due to results not fitting in one page', () => { - const tableWrapper = mount(
) - const paginationWrapper = tableWrapper.find('.pagination') - const nextPageButton = paginationWrapper.find('#nextPageButton') - expect(nextPageButton.props().disabled).toBe(false) + expect(firstRowSecondColWrapper.text()).toEqual('Mickey') }) - it('Clicking on previous/next page button changes results', () => { - let bodyWrapper = null - let rows = null - const tableWrapper = mount(
) - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(10) - const paginationWrapper = tableWrapper.find('.pagination') - const nextPageButton = paginationWrapper.find('#nextPageButton') - nextPageButton.simulate('click') - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(5) - const previousPageButton = paginationWrapper.find('#previousPageButton') - previousPageButton.simulate('click') - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(10) - }) + it('should format cell content when a formatter is provided', () => { + const tableWrapper = mount(
) + const bodyWrapper = tableWrapper.find('tbody') + const firstRowFourthColWrapper = bodyWrapper.find('tr').at(0).find('td').at(3) - it('Clicking on first/last page button changes results', () => { - let bodyWrapper = null - let rows = null - const tableWrapper = mount(
) - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(10) - const paginationWrapper = tableWrapper.find('.pagination') - const lastPageButton = paginationWrapper.find('#lastPageButton') - lastPageButton.simulate('click') - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(5) - const firstPageButton = paginationWrapper.find('#firstPageButton') - firstPageButton.simulate('click') - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(10) + expect( + firstRowFourthColWrapper.containsMatchingElement( +
    +
  • milk
  • +
  • tea
  • +
, + ), + ).toBeTruthy() }) - it('Changing page number changes results', () => { - const tableWrapper = mount(
) - const paginationWrapper = tableWrapper.find('.pagination') - const pageNumberInput = paginationWrapper.find('input') - pageNumberInput.simulate('change', { target: { value: 2 } }) + it('should call the onClick callback when a row is clicked', () => { + const tableWrapper = mount( +
, + ) const bodyWrapper = tableWrapper.find('tbody') - const rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(5) - }) + const rowWrapper = bodyWrapper.find('tr').at(0) + rowWrapper.simulate('click') - it('Changing page size changes results', () => { - const tableWrapper = mount(
) - const paginationWrapper = tableWrapper.find('.pagination') - const pageSizeSelect = paginationWrapper.find('select') - pageSizeSelect.simulate('change', { target: { value: 20 } }) - const bodyWrapper = tableWrapper.find('tbody') - const rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(15) + expect(onRowClickSpy).toHaveBeenCalledTimes(1) }) - it('Filtering changes results', () => { - let bodyWrapper = null - let rows = null - const tableWrapper = mount(
) - const tableWrapperV3 = mount( -
, - ) - expect(tableWrapperV3).toBeTruthy() - const tableWrapperV4 = mount( -
, + it('should call the onClick callback when an action button is clicked', () => { + const tableWrapper = mount( +
, ) - expect(tableWrapperV4).toBeTruthy() - const tableHeaderWrapper = tableWrapper.find('thead') - const nameColumnWrapper = tableHeaderWrapper.find('th').first() - const inputFilter = nameColumnWrapper.find('input') - inputFilter.simulate('change', { target: { value: 'mariah' } }) - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(1) - inputFilter.simulate('change', { target: { value: null } }) - const selectFilter = tableHeaderWrapper.find('select') - selectFilter.simulate('change', { target: { value: true } }) - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(9) - selectFilter.simulate('change', { target: { value: 'All' } }) - const dateFilterWrapper = tableHeaderWrapper.find('.react-datepicker-wrapper') - const dateFilter = dateFilterWrapper.find('input') - dateFilter.simulate('change', { target: { value: '01/01/2020' } }) - bodyWrapper = tableWrapper.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(0) - }) + const bodyWrapper = tableWrapper.find('tbody') + const rowWrapper = bodyWrapper.find('tr').at(0) + const cellWrappers = rowWrapper.find('td') + const actionsCellWrapper = cellWrappers.at(cellWrappers.length - 1) - it('Filtering changes results, testing with undefinedMeansFalse', () => { - let bodyWrapper = null - let rows = null - const tableWrapperV2 = mount( -
, - ) - const tableWrapperV5 = mount( -
, - ) - expect(tableWrapperV5).toBeTruthy() - const tableWrapperV6 = mount( -
, - ) - expect(tableWrapperV6).toBeTruthy() - const tableWrapperV7 = mount( -
, - ) - expect(tableWrapperV7).toBeTruthy() - const tableHeaderWrapper = tableWrapperV2.find('thead') - const nameColumnWrapper = tableHeaderWrapper.find('th').first() - const inputFilter = nameColumnWrapper.find('input') - inputFilter.simulate('change', { target: { value: 'mariah' } }) - bodyWrapper = tableWrapperV2.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(1) - inputFilter.simulate('change', { target: { value: null } }) - const selectFilter = tableHeaderWrapper.find('select') - selectFilter.simulate('change', { target: { value: true } }) - bodyWrapper = tableWrapperV2.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(9) - selectFilter.simulate('change', { target: { value: 'All' } }) - const dateFilterWrapper = tableHeaderWrapper.find('.react-datepicker-wrapper') - const dateFilter = dateFilterWrapper.find('input') - dateFilter.simulate('change', { target: { value: '01/01/2020' } }) - bodyWrapper = tableWrapperV2.find('tbody') - rows = bodyWrapper.find('tr') - expect(rows).toHaveLength(0) + const action0ButtonWrapper = actionsCellWrapper.find(Button).at(0) + action0ButtonWrapper.simulate('click') + expect(onClickAction0Spy).toHaveBeenCalledTimes(1) + + const action1ButtonWrapper = actionsCellWrapper.find(Button).at(1) + action1ButtonWrapper.simulate('click') + expect(onClickAction1Spy).toHaveBeenCalledTimes(1) }) }) From 5ebf738c0388af7194671aa3eb63eef70ab532ba Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 18 Jun 2020 03:32:16 +0000 Subject: [PATCH 3/7] chore(release): 1.13.0 [skip ci] # [1.13.0](https://github.com/HospitalRun/components/compare/v1.12.2...v1.13.0) (2020-06-18) ### Features * **table:** update table ([#455](https://github.com/HospitalRun/components/issues/455)) ([7ff5648](https://github.com/HospitalRun/components/commit/7ff56485ed7a67af9e7c1808c49a28cc0e02a21e)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b35767..118115df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [1.13.0](https://github.com/HospitalRun/components/compare/v1.12.2...v1.13.0) (2020-06-18) + + +### Features + +* **table:** update table ([#455](https://github.com/HospitalRun/components/issues/455)) ([7ff5648](https://github.com/HospitalRun/components/commit/7ff56485ed7a67af9e7c1808c49a28cc0e02a21e)) + ## [1.12.2](https://github.com/HospitalRun/components/compare/v1.12.1...v1.12.2) (2020-06-16) diff --git a/package.json b/package.json index cc146664..f2931ac3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hospitalrun/components", - "version": "1.12.2", + "version": "1.13.0", "license": "MIT", "funding": { "type": "opencollective", From 2e5af679b88f230726e34d87666acef78c8d6d63 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 12:17:00 +0000 Subject: [PATCH 4/7] chore(deps): bump date-fns from 2.9.0 to 2.14.0 Bumps [date-fns](https://github.com/date-fns/date-fns) from 2.9.0 to 2.14.0. - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v2.9.0...v2.14.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2931ac3..69060602 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "@types/react-datepicker": "~3.0.0", "babel-jest": "~26.0.1", "chart.js": "~2.9.3", - "date-fns": "~2.9.0", + "date-fns": "~2.14.0", "formik": "~2.1.0", "lodash": "~4.17.15", "moment": "~2.26.0", From 4cea65515d8d76e125b3a30597624f3c60037820 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2020 21:26:52 +0200 Subject: [PATCH 5/7] chore(deps-dev): bump eslint-plugin-jsx-a11y from 6.2.3 to 6.3.0 (#466) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 69060602..0c6e2cad 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "eslint-config-prettier": "~6.11.0", "eslint-plugin-import": "~2.21.1", "eslint-plugin-jest": "~23.13.1", - "eslint-plugin-jsx-a11y": "~6.2.3", + "eslint-plugin-jsx-a11y": "~6.3.0", "eslint-plugin-prettier": "~3.1.1", "eslint-plugin-react": "~7.20.0", "eslint-plugin-react-hooks": "~4.0.1", From a8e1e505079e1c7cfacb127c15c2422c93019310 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2020 12:01:06 +0000 Subject: [PATCH 6/7] chore(deps-dev): bump eslint-config-airbnb from 18.1.0 to 18.2.0 Bumps [eslint-config-airbnb](https://github.com/airbnb/javascript) from 18.1.0 to 18.2.0. - [Release notes](https://github.com/airbnb/javascript/releases) - [Commits](https://github.com/airbnb/javascript/compare/eslint-config-airbnb-v18.1.0...eslint-config-airbnb-v18.2.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c6e2cad..cdd7cf9a 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "enzyme": "~3.11.0", "enzyme-adapter-react-16": "~1.15.2", "eslint": "~7.2.0", - "eslint-config-airbnb": "~18.1.0", + "eslint-config-airbnb": "~18.2.0", "eslint-config-prettier": "~6.11.0", "eslint-plugin-import": "~2.21.1", "eslint-plugin-jest": "~23.13.1", From de2431e880a0117922620df546b1d5eff82f21d6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2020 12:20:12 +0000 Subject: [PATCH 7/7] chore(deps): bump moment from 2.26.0 to 2.27.0 Bumps [moment](https://github.com/moment/moment) from 2.26.0 to 2.27.0. - [Release notes](https://github.com/moment/moment/releases) - [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md) - [Commits](https://github.com/moment/moment/compare/2.26.0...2.27.0) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c6e2cad..7f0487f7 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "date-fns": "~2.14.0", "formik": "~2.1.0", "lodash": "~4.17.15", - "moment": "~2.26.0", + "moment": "~2.27.0", "react-bootstrap-typeahead": "^4.2.3", "react-datepicker": "~3.0.0", "react-spinners": "~0.8.3",