Repository: laboratoriobridge/bold
Pull request #478 (2019-10-02 21:14:41)

Add 'align' prop to TableColumnConfig
closes https://github.com/laboratoriobridge/bold/issues/477


Patch:
diff --git a/site/pages/components/table/DataTable.demo.tsx b/site/pages/components/table/DataTable.demo.tsx
index 4a1fefcbc..39b117c9e 100644
--- a/site/pages/components/table/DataTable.demo.tsx
+++ b/site/pages/components/table/DataTable.demo.tsx
@@ -48,12 +48,12 @@ function DataTableExample() {
         },
         {
           name: 'actions',
+          align: 'right',
           render: item => (
             <Button size='small' skin='ghost'>
               <Icon icon='penOutline' />
             </Button>
           ),
-          style: { textAlign: 'right' },
         },
       ]}
     />
diff --git a/site/pages/components/table/PagedTable.demo.tsx b/site/pages/components/table/PagedTable.demo.tsx
index cce872e4e..b38c7b391 100644
--- a/site/pages/components/table/PagedTable.demo.tsx
+++ b/site/pages/components/table/PagedTable.demo.tsx
@@ -67,12 +67,12 @@ function PagedTableExample() {
         },
         {
           name: 'actions',
+          align: 'right',
           render: item => (
             <Button size='small' skin='ghost'>
               <Icon icon='penOutline' />
             </Button>
           ),
-          style: { textAlign: 'right' },
         },
       ]}
     />
diff --git a/src/components/Table/DataTable/DataTable.stories.tsx b/src/components/Table/DataTable/DataTable.stories.tsx
index a1f61c4d1..45f999525 100644
--- a/src/components/Table/DataTable/DataTable.stories.tsx
+++ b/src/components/Table/DataTable/DataTable.stories.tsx
@@ -33,7 +33,7 @@ storiesOf('Components|Table', module).add('DataTable', () => (
       { name: 'age', header: 'Age', render: (row: Row) => row.age },
       {
         name: 'buttons',
-        style: { textAlign: 'right', whiteSpace: 'nowrap' },
+        align: 'right',
         render: (row: Row) => (
           <>
             <Button size='small' skin='ghost'>
diff --git a/src/components/Table/DataTable/DataTable.tsx b/src/components/Table/DataTable/DataTable.tsx
index d359aed3c..03b68243e 100644
--- a/src/components/Table/DataTable/DataTable.tsx
+++ b/src/components/Table/DataTable/DataTable.tsx
@@ -1,6 +1,6 @@
 import React from 'react'
 
-import { ExternalStyles } from '../../../styles'
+import { ExternalStyles, useCss } from '../../../styles'
 import { SortDirection } from '../SortableLabel/SortableLabel'
 import { Table, TableHead, TableHeader, TableHeaderProps, TableProps, TableRow } from '../Table'
 
@@ -11,6 +11,7 @@ export interface TableColumnConfig<T = any> {
   header?: React.ReactNode
   sortable?: boolean
   style?: ExternalStyles
+  align?: 'left' | 'center' | 'right'
   render(row: T): React.ReactNode
 }
 
@@ -30,6 +31,8 @@ export interface DataTableRenderProps extends DataTableProps {
 }
 
 export function DataTable<T = any>(props: DataTableProps<T>) {
+  const { css } = useCss()
+
   const getColumn = (columnName: string): TableColumnConfig => {
     return props.columns.find(col => col.name === columnName)
   }
@@ -41,12 +44,15 @@ export function DataTable<T = any>(props: DataTableProps<T>) {
       throw new Error(`Column '${column}' not found.`)
     }
 
+    const { name, sortable, style } = col
+
     return {
-      key: col.name,
-      'data-name': col.name,
-      sortable: col.sortable,
+      key: name,
+      'data-name': name,
+      sortable,
       sortDirection: getSortDirection(col),
       onSortChange: handleSortChange(col),
+      style: css(defaultColumnStyles(col), style),
     }
   }
 
@@ -122,3 +128,7 @@ const changeSort = (sort: string[], name: string, dir: SortDirection): string[]
   })
   return swap ? newArray : [...newArray, newSort]
 }
+
+export const defaultColumnStyles = (col: TableColumnConfig) => ({
+  textAlign: col.align,
+})
diff --git a/src/components/Table/DataTable/TableFilledBody.tsx b/src/components/Table/DataTable/TableFilledBody.tsx
index 6578257ea..9a9b777a6 100644
--- a/src/components/Table/DataTable/TableFilledBody.tsx
+++ b/src/components/Table/DataTable/TableFilledBody.tsx
@@ -1,39 +1,37 @@
 import React from 'react'
 
+import { useCss } from '../../../styles'
 import { TableBody, TableCell, TableRow } from '../Table'
 
-import { DataTableProps } from './DataTable'
+import { DataTableProps, defaultColumnStyles } from './DataTable'
 import { TableLoadingRow } from './TableLoadingRow'
 import { TablePlaceholderRow } from './TablePlaceholderRow'
 
 export interface TableFilledBodyProps extends Pick<DataTableProps, 'columns' | 'rows' | 'loading' | 'onRowClick'> {}
 
-export class TableFilledBody extends React.PureComponent<TableFilledBodyProps> {
-  render() {
-    const { columns, rows, loading, onRowClick } = this.props
-
-    return (
-      <TableBody>
-        {loading && <TableLoadingRow colSpan={columns.length} />}
-
-        {!loading && this.isEmpty() && <TablePlaceholderRow colSpan={columns.length} />}
-
-        {rows.map((row, idx) => (
-          <TableRow key={idx} onClick={onRowClick && this.handleClick(row)}>
-            {columns.map((col, colIdx) => (
-              <TableCell key={colIdx} style={col.style}>
-                {col.render(row)}
-              </TableCell>
-            ))}
-          </TableRow>
-        ))}
-      </TableBody>
-    )
-  }
-
-  private handleClick = row => e => {
-    this.props.onRowClick(row)
-  }
-
-  private isEmpty = () => !this.props.rows || this.props.rows.length === 0
+export function TableFilledBody(props: TableFilledBodyProps) {
+  const { columns, rows, loading, onRowClick } = props
+  const { css } = useCss()
+
+  const handleClick = row => e => onRowClick(row)
+
+  const isEmpty = () => !rows || rows.length === 0
+
+  return (
+    <TableBody>
+      {loading && <TableLoadingRow colSpan={columns.length} />}
+
+      {!loading && isEmpty() && <TablePlaceholderRow colSpan={columns.length} />}
+
+      {rows.map((row, idx) => (
+        <TableRow key={idx} onClick={onRowClick && handleClick(row)}>
+          {columns.map((col, colIdx) => (
+            <TableCell key={colIdx} style={css(defaultColumnStyles(col), col.style)}>
+              {col.render(row)}
+            </TableCell>
+          ))}
+        </TableRow>
+      ))}
+    </TableBody>
+  )
 }


Test patch:
diff --git a/src/components/Table/DataTable/DataTable.test.tsx b/src/components/Table/DataTable/DataTable.test.tsx
index 29e47faec..b080dcd6d 100644
--- a/src/components/Table/DataTable/DataTable.test.tsx
+++ b/src/components/Table/DataTable/DataTable.test.tsx
@@ -17,7 +17,7 @@ const rows: Row[] = [
 ]
 
 const sortHandler = jest.fn()
-const createTable = (props: Partial<DataTableProps> = {}) => (
+const DataTableTest = (props: Partial<DataTableProps>) => (
   // tslint:disable jsx-no-lambda
   <DataTable
     rows={rows}
@@ -30,7 +30,8 @@ const createTable = (props: Partial<DataTableProps> = {}) => (
         name: 'age',
         header: 'Age',
         sortable: true,
-        style: { textAlign: 'right' },
+        align: 'right',
+        style: { color: 'red' },
         render: (row: Row) => row.age,
       },
     ]}
@@ -39,12 +40,12 @@ const createTable = (props: Partial<DataTableProps> = {}) => (
 )
 
 it('should render correctly', () => {
-  const { container } = render(createTable())
+  const { container } = render(<DataTableTest />)
   expect(container).toMatchSnapshot()
 })
 
 it('should call onSortChange with right parameters when clicked over column title', () => {
-  const { container } = render(createTable())
+  const { container } = render(<DataTableTest />)
   fireEvent.click(container.querySelector('th[data-name="id"] span'))
   expect(sortHandler).toHaveBeenLastCalledWith(['-id'])
 
@@ -66,7 +67,7 @@ it('should render TableLoadingRow when loading prop is on', () => {
 
 it('should accept the render prop', () => {
   const renderSpy = jest.fn()
-  render(createTable({ render: renderSpy }))
+  render(<DataTableTest render={renderSpy} />)
   expect(renderSpy).toHaveBeenCalledWith(
     expect.objectContaining({
       columns: expect.anything(),
@@ -77,7 +78,7 @@ it('should accept the render prop', () => {
 describe('onRowClick prop', () => {
   it('should call the prop with the clicked row', () => {
     const clickHandler = jest.fn()
-    const { container } = render(createTable({ onRowClick: clickHandler }))
+    const { container } = render(<DataTableTest onRowClick={clickHandler} />)
     const trs = container.querySelectorAll('tbody tr')
 
     expect(clickHandler).not.toHaveBeenCalled()
@@ -92,7 +93,7 @@ describe('onRowClick prop', () => {
 
 describe('getColumn', () => {
   const renderSpy = jest.fn()
-  render(createTable({ render: renderSpy }))
+  render(<DataTableTest render={renderSpy} />)
   const getColumn = renderSpy.mock.calls[0][0].getColumn
 
   it('should return the column configuration by its name', () => {
@@ -107,7 +108,7 @@ describe('getColumn', () => {
 
 describe('getHeaderProps', () => {
   const renderSpy = jest.fn()
-  render(createTable({ render: renderSpy }))
+  render(<DataTableTest render={renderSpy} />)
   const getHeaderProps = renderSpy.mock.calls[0][0].getHeaderProps
 
   it('should return the table header props of a column configuration', () => {
@@ -115,6 +116,7 @@ describe('getHeaderProps', () => {
       name: 'id',
       render: () => null,
       sortable: true,
+      align: 'right',
     })
 
     expect(config).toMatchObject({
diff --git a/src/components/Table/DataTable/__snapshots__/DataTable.test.tsx.snap b/src/components/Table/DataTable/__snapshots__/DataTable.test.tsx.snap
index 718cb1f81..0604aeab4 100644
--- a/src/components/Table/DataTable/__snapshots__/DataTable.test.tsx.snap
+++ b/src/components/Table/DataTable/__snapshots__/DataTable.test.tsx.snap
@@ -1,19 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-.emotion-4 {
-  text-align: left;
-  padding: 0.5rem 1rem;
-  border-bottom: 1px solid #D3D4DD;
-}
-
-.emotion-17 {
-  text-align: left;
-  padding: 0 1rem;
-  height: 2.5rem;
-  border-bottom: 1px solid #D3D4DD;
-}
-
 .emotion-30 {
   width: 100%;
   border-collapse: collapse;
@@ -24,6 +11,12 @@ exports[`should render correctly 1`] = `
   background: #F0F0F5;
 }
 
+.emotion-4 {
+  text-align: left;
+  padding: 0.5rem 1rem;
+  border-bottom: 1px solid #D3D4DD;
+}
+
 .emotion-3 {
   display: -webkit-inline-box;
   display: -webkit-inline-flex;
@@ -46,12 +39,28 @@ exports[`should render correctly 1`] = `
   fill: #0069D0;
 }
 
+.emotion-14 {
+  text-align: left;
+  padding: 0.5rem 1rem;
+  border-bottom: 1px solid #D3D4DD;
+  text-align: right;
+  color: red;
+}
+
+.emotion-17 {
+  text-align: left;
+  padding: 0 1rem;
+  height: 2.5rem;
+  border-bottom: 1px solid #D3D4DD;
+}
+
 .emotion-19 {
   text-align: left;
   padding: 0 1rem;
   height: 2.5rem;
   border-bottom: 1px solid #D3D4DD;
   text-align: right;
+  color: red;
 }
 
 <div>
@@ -115,7 +124,7 @@ exports[`should render correctly 1`] = `
           </span>
         </th>
         <th
-          class="emotion-4"
+          class="emotion-14"
           data-name="age"
         >
           <span
diff --git a/src/components/Table/PagedTable/__snapshots__/PagedTable.test.tsx.snap b/src/components/Table/PagedTable/__snapshots__/PagedTable.test.tsx.snap
index 86b2b7872..68997bcee 100644
--- a/src/components/Table/PagedTable/__snapshots__/PagedTable.test.tsx.snap
+++ b/src/components/Table/PagedTable/__snapshots__/PagedTable.test.tsx.snap
@@ -1,19 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-.emotion-4 {
-  text-align: left;
-  padding: 0.5rem 1rem;
-  border-bottom: 1px solid #D3D4DD;
-}
-
-.emotion-13 {
-  text-align: left;
-  padding: 0 1rem;
-  height: 2.5rem;
-  border-bottom: 1px solid #D3D4DD;
-}
-
 .emotion-26 {
   width: 100%;
   border-collapse: collapse;
@@ -24,6 +11,12 @@ exports[`should render correctly 1`] = `
   background: #F0F0F5;
 }
 
+.emotion-4 {
+  text-align: left;
+  padding: 0.5rem 1rem;
+  border-bottom: 1px solid #D3D4DD;
+}
+
 .emotion-3 {
   display: -webkit-inline-box;
   display: -webkit-inline-flex;
@@ -46,6 +39,13 @@ exports[`should render correctly 1`] = `
   fill: #0069D0;
 }
 
+.emotion-13 {
+  text-align: left;
+  padding: 0 1rem;
+  height: 2.5rem;
+  border-bottom: 1px solid #D3D4DD;
+}
+
 .emotion-27 {
   font-weight: bold;
   display: -webkit-box;