From 7d2b33a0f786178c8f0e968b4950df697c1ad727 Mon Sep 17 00:00:00 2001 From: Mike Wislek <49659689+mwislek@users.noreply.github.com> Date: Tue, 14 Jul 2020 13:59:37 -0500 Subject: [PATCH] Add valueGetter to Table component (#434) * Add valueGetter to Table component * Manually update documentation --- docs.md | 2 ++ package.json | 2 +- src/tables/components/table-row.js | 18 ++++++++++-------- src/tables/helpers/column-prop-types.js | 1 + src/tables/sortable-table.js | 23 ++++++++++++----------- src/tables/table-column.js | 5 +++-- stories/tables/sortable-table.story.js | 14 +++++++++++++- test/tables/sortable-table.test.js | 22 +++++++++++++++++++--- 8 files changed, 61 insertions(+), 26 deletions(-) diff --git a/docs.md b/docs.md index 594fe357..0c6371fa 100644 --- a/docs.md +++ b/docs.md @@ -1316,6 +1316,7 @@ This component's behavior is largely determined by the [TableColumn][101] compon - `data` **[Array][145]** An array of objects to display in the table- one object per row (optional, default `[]`) - `initialColumn` **[Number][144]** The name of the column that's initially selected (optional, default `''`) +- `initialAscending` **[Boolean][142]** The sort direction of the initial column (optional, default `true`) - `disableReverse` **[Boolean][142]** Disables automatic reversing of descending sorts (optional, default `false`) - `disableSort` **[Boolean][142]** A flag to disable sorting on all columns and hide sorting arrows. (optional, default `false`) - `controlled` **[Boolean][142]** A flag to disable sorting on all columns, while keeping the sorting arrows. Used when sorting is controlled by an external source. (optional, default `false`) @@ -1352,6 +1353,7 @@ A component used to pass column information to a [Table][95] or [SortableTable][ - `format` **[Function][141]?** A function that formats the value displayed in each cell in the column - `disabled` **[Boolean][142]?** A flag that disables sorting for the column - `placeholder` **[String][140]?** A string that will be displayed if the value of the cell is `undefined` or `null` +- `valueGetter` **[Function][141]?** A function that will return a cell's value derived from each data object. Will be passed the `data` for the row. ### Examples diff --git a/package.json b/package.json index f847beb0..b4f1470f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@launchpadlab/lp-components", - "version": "3.31.4", + "version": "3.32.0", "engines": { "node": "^8.0.0 || ^10.13.0 || ^12.0.0" }, diff --git a/src/tables/components/table-row.js b/src/tables/components/table-row.js index 146f95c4..4f753f1e 100644 --- a/src/tables/components/table-row.js +++ b/src/tables/components/table-row.js @@ -22,16 +22,18 @@ function TableRow ({ { columns.map((column, key) => { - const { name, component: CellComponent=DefaultCellComponent, format=identity, onClick=noop, ...rest } = column - const value = format(get(name, rowData)) + const { name, component: CellComponent=DefaultCellComponent, format=identity, onClick=noop, valueGetter, ...rest } = column + const cellValue = + valueGetter ? valueGetter(rowData) : get(name, rowData) + const value = format(cellValue) const onColClick = column.disabled ? noop : () => onClick(rowData) return }) } diff --git a/src/tables/helpers/column-prop-types.js b/src/tables/helpers/column-prop-types.js index cab16d39..cc2fe914 100644 --- a/src/tables/helpers/column-prop-types.js +++ b/src/tables/helpers/column-prop-types.js @@ -11,6 +11,7 @@ export const columnPropTypes = { headerComponent: componentType, onClick: PropTypes.func, format: PropTypes.func, + valueGetter: PropTypes.func, } export const Types = { diff --git a/src/tables/sortable-table.js b/src/tables/sortable-table.js index 918be19d..f7a77a5f 100644 --- a/src/tables/sortable-table.js +++ b/src/tables/sortable-table.js @@ -2,25 +2,26 @@ import React from 'react' import PropTypes from 'prop-types' import { sortable, sortablePropTypes, noop } from '../utils' import { getColumnData, Types } from './helpers' -import { TableHeader as DefaultHeader, TableRow as Row } from './components' +import { TableHeader as DefaultHeader, TableRow as Row } from './components' import classnames from 'classnames' /** * A component for displaying sortable data in a table. * This component's behavior is largely determined by the {@link TableColumn} components that are passed to it. - * + * * @name SortableTable * @type Function * @param {Array} [data=[]] - An array of objects to display in the table- one object per row * @param {Number} [initialColumn=''] - The name of the column that's initially selected + * @param {Boolean} [initialAscending=true] - The sort direction of the initial column * @param {Boolean} [disableReverse=false] - Disables automatic reversing of descending sorts * @param {Boolean} [disableSort=false] - A flag to disable sorting on all columns and hide sorting arrows. - * @param {Boolean} [controlled=false] - A flag to disable sorting on all columns, while keeping the sorting arrows. Used when sorting is controlled by an external source. + * @param {Boolean} [controlled=false] - A flag to disable sorting on all columns, while keeping the sorting arrows. Used when sorting is controlled by an external source. * @param {Function} [onChange] - A callback that will be fired when the sorting state changes * @param {Function} [rowComponent] - A custom row component for the table. Will be passed the `data` for the row, as well as `children` to render. * @param {Function} [headerComponent] - A custom header component for the table. Will be passed the configuration of the corresponding column, as well as the current `sortPath` / `ascending` and an `onClick` handler. May be overridden by a custom `headerComponent` for a column. * @example - * + * * function PersonTable ({ people }) { * return ( * @@ -49,15 +50,15 @@ const defaultProps = { className: '' } -function SortableTable ({ +function SortableTable ({ columns, - data: unsortedData, + data: unsortedData, disableSort, controlled, - sort, - ascending, - sortPath, - setSortPath, + sort, + ascending, + sortPath, + setSortPath, setSortFunc, rowComponent, headerComponent, @@ -91,7 +92,7 @@ function SortableTable ({ { - data.map((rowData, key) => + data.map((rowData, key) => diff --git a/stories/tables/sortable-table.story.js b/stories/tables/sortable-table.story.js index 628e0f99..558f7482 100644 --- a/stories/tables/sortable-table.story.js +++ b/stories/tables/sortable-table.story.js @@ -34,6 +34,10 @@ function CustomHeader ({ column: { name } }) { ) } +function createCustomValue(data) { + return `${data.name}:${data.age}` +} + storiesOf('SortableTable', module) .add('default', () => ( @@ -103,4 +107,12 @@ storiesOf('SortableTable', module) val.toFixed(1) } /> val === 'yes' ? 'Y' : 'N' } /> - )) \ No newline at end of file + )) + .add('with custom value getter', () => ( + + + val.toFixed(1) } /> + val === 'yes' ? 'Y' : 'N' } /> + + + )) diff --git a/test/tables/sortable-table.test.js b/test/tables/sortable-table.test.js index 997f51ec..d6c2b2ca 100644 --- a/test/tables/sortable-table.test.js +++ b/test/tables/sortable-table.test.js @@ -135,7 +135,7 @@ test('column can have custom className', () => { }) test('column can have custom cell component', () => { - const MyCell = () => Hi! + const MyCell = () => Hi! const wrapper = mount( @@ -232,7 +232,7 @@ test('`format` updates the cell value', () => { test('`placeholder` option is displayed if value is `null` or `undefined`', () => { const data = [ - { name: null }, + { name: null }, { name: undefined }, ] const wrapper = mount( @@ -246,7 +246,7 @@ test('`placeholder` option is displayed if value is `null` or `undefined`', () = test('can recieve custom class name', () => { const data = [ - { name: null }, + { name: null }, { name: undefined }, ] const wrapper = mount( @@ -256,3 +256,19 @@ test('can recieve custom class name', () => { ) expect(wrapper.find('table.foo').exists()).toBe(true) }) + +test('`valueGetter` derives the cell value', () => { + const data = [ + { name: 'Opportunity 1', accountName: 'Dealer 1' }, + { name: 'Opportunity 2', accountName: 'Dealer 2' }, + ] + const myValueGetter = jest.fn((data) => `${data.name} - ${data.accountName}`) + const wrapper = mount( + + + + ) + expect(wrapper.find('td').first().text()).toEqual('Opportunity 1 - Dealer 1') + expect(wrapper.find('td').last().text()).toEqual('Opportunity 2 - Dealer 2') + expect(myValueGetter).toHaveBeenCalled() +})