test: snapshots for useFilters, useGroupBy, useSortBy

This commit is contained in:
tannerlinsley 2019-07-30 15:57:11 -06:00
parent bcefe5d6e8
commit 12e7b3220d
11 changed files with 848 additions and 80 deletions

View File

@ -58,26 +58,17 @@ function Table({ columns, data }) {
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(
column =>
console.log(column) || (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
>
{column.render('Header')}
{/* Add a sort direction indicator */}
<span>
{column.sorted
? column.sortedDesc
? ' 🔽'
: ' 🔼'
: ''}
</span>
</th>
)
)}
{headerGroup.headers.map(column => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
{/* Add a sort direction indicator */}
<span>
{column.sorted ? (column.sortedDesc ? ' 🔽' : ' 🔼') : ''}
</span>
</th>
))}
</tr>
))}
</thead>

View File

@ -1,12 +1,11 @@
export const text = (rows, id, filterValue) => {
return rows.filter(row => {
rows = rows.filter(row => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.includes(String(filterValue).toLowerCase())
: true
return String(rowValue)
.toLowerCase()
.includes(String(filterValue).toLowerCase())
})
return rows
}
text.autoRemove = val => !val

View File

@ -1,6 +1,5 @@
import '@testing-library/react/cleanup-after-each'
import '@testing-library/jest-dom/extend-expect'
// NOTE: jest-dom adds handy assertions to Jest and is recommended, but not required
import React from 'react'
import { render } from '@testing-library/react'

View File

@ -63,6 +63,7 @@ export const useTable = (props, ...plugins) => {
plugins,
hooks: {
columnsBeforeHeaderGroups: [],
columnsBeforeHeaderGroupsDeps: [],
useMain: [],
useColumns: [],
useHeaders: [],
@ -84,45 +85,57 @@ export const useTable = (props, ...plugins) => {
})
if (debug) console.timeEnd('plugins')
// Compute columns, headerGroups and headers
const columnInfo = React.useMemo(
if (debug) console.info('buildColumns/headerGroup/headers')
// Decorate All the columns
let columnTree = React.useMemo(
() => decorateColumnTree(userColumns, defaultColumn),
[defaultColumn, userColumns]
)
// Get the flat list of all columns
let columns = React.useMemo(() => flattenBy(columnTree, 'columns'), [
columnTree,
])
// Allow hooks to decorate columns (and trigger this memoization via deps)
columns = React.useMemo(
() => {
if (debug) console.info('buildColumns/headerGroup/headers')
// Decorate All the columns
let columnTree = decorateColumnTree(userColumns, defaultColumn)
// Get the flat list of all columns
let columns = flattenBy(columnTree, 'columns')
// Allow hooks to decorate columns
if (debug) console.time('hooks.columnsBeforeHeaderGroups')
columns = applyHooks(
const newColumns = applyHooks(
instanceRef.current.hooks.columnsBeforeHeaderGroups,
columns,
instanceRef.current
)
if (debug) console.timeEnd('hooks.columnsBeforeHeaderGroups')
// Make the headerGroups
const headerGroups = makeHeaderGroups(
columns,
findMaxDepth(columnTree),
defaultColumn
)
const headers = flattenBy(headerGroups, 'headers')
return {
columns,
headerGroups,
headers,
}
return newColumns
},
[debug, defaultColumn, userColumns]
[
columns,
debug,
// eslint-disable-next-line react-hooks/exhaustive-deps
...applyHooks(
instanceRef.current.hooks.columnsBeforeHeaderGroupsDeps,
[],
instanceRef.current
),
]
)
// Place the columns, headerGroups and headers on the api
Object.assign(instanceRef.current, columnInfo)
// Make the headerGroups
const headerGroups = React.useMemo(
() => makeHeaderGroups(columns, findMaxDepth(columnTree), defaultColumn),
[columnTree, columns, defaultColumn]
)
const headers = React.useMemo(() => flattenBy(headerGroups, 'headers'), [
headerGroups,
])
Object.assign(instanceRef.current, {
columns,
headerGroups,
headers,
})
// Access the row model
instanceRef.current.rows = React.useMemo(

View File

@ -1,11 +1,203 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders a sortable table 1`] = `
exports[`renders a filterable table 1`] = `
"Snapshot Diff:
Compared values have no visual difference."
- First value
+ Second value
@@ -37,11 +37,11 @@
colspan=\\"1\\"
>
Last Name
<input
placeholder=\\"Search...\\"
- value=\\"\\"
+ value=\\"l\\"
/>
</th>
<th
class=\\"\\"
colspan=\\"1\\"
@@ -115,78 +115,10 @@
</td>
<td
class=\\"\\"
>
progress: 50
- </td>
- </tr>
- <tr
- class=\\"\\"
- >
- <td
- class=\\"\\"
- >
- firstName: derek
- </td>
- <td
- class=\\"\\"
- >
- lastName: perkins
- </td>
- <td
- class=\\"\\"
- >
- age: 40
- </td>
- <td
- class=\\"\\"
- >
- visits: 40
- </td>
- <td
- class=\\"\\"
- >
- status: Single
- </td>
- <td
- class=\\"\\"
- >
- progress: 80
- </td>
- </tr>
- <tr
- class=\\"\\"
- >
- <td
- class=\\"\\"
- >
- firstName: joe
- </td>
- <td
- class=\\"\\"
- >
- lastName: bergevin
- </td>
- <td
- class=\\"\\"
- >
- age: 45
- </td>
- <td
- class=\\"\\"
- >
- visits: 20
- </td>
- <td
- class=\\"\\"
- >
- status: Complicated
- </td>
- <td
- class=\\"\\"
- >
- progress: 10
</td>
</tr>
<tr
class=\\"\\"
>"
`;
exports[`renders a sortable table 2`] = `
exports[`renders a filterable table 2`] = `
"Snapshot Diff:
Compared values have no visual difference."
- First value
+ Second value
@@ -37,11 +37,11 @@
colspan=\\"1\\"
>
Last Name
<input
placeholder=\\"Search...\\"
- value=\\"l\\"
+ value=\\"er\\"
/>
</th>
<th
class=\\"\\"
colspan=\\"1\\"
@@ -89,70 +89,70 @@
class=\\"\\"
>
<td
class=\\"\\"
>
- firstName: tanner
+ firstName: derek
</td>
<td
class=\\"\\"
>
- lastName: linsley
+ lastName: perkins
</td>
<td
class=\\"\\"
>
- age: 29
+ age: 40
</td>
<td
class=\\"\\"
>
- visits: 100
+ visits: 40
</td>
<td
class=\\"\\"
>
- status: In Relationship
+ status: Single
</td>
<td
class=\\"\\"
>
- progress: 50
+ progress: 80
</td>
</tr>
<tr
class=\\"\\"
>
<td
class=\\"\\"
>
- firstName: jaylen
+ firstName: joe
</td>
<td
class=\\"\\"
>
- lastName: linsley
+ lastName: bergevin
</td>
<td
class=\\"\\"
>
- age: 26
+ age: 45
</td>
<td
class=\\"\\"
>
- visits: 99
+ visits: 20
</td>
<td
class=\\"\\"
>
- status: In Relationship
+ status: Complicated
</td>
<td
class=\\"\\"
>
- progress: 70
+ progress: 10
</td>
</tr>
</tbody>
</table>
</DocumentFragment>"
`;

View File

@ -0,0 +1,371 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders a filterable table 1`] = `
"Snapshot Diff:
- First value
+ Second value
@@ -29,13 +29,13 @@
<span
class=\\"\\"
style=\\"cursor: pointer;\\"
title=\\"Toggle GroupBy\\"
>
- 👊
+ 🛑
</span>
- First Name
+ Last Name
</th>
<th
class=\\"\\"
colspan=\\"1\\"
>
@@ -44,11 +44,11 @@
style=\\"cursor: pointer;\\"
title=\\"Toggle GroupBy\\"
>
👊
</span>
- Last Name
+ First Name
</th>
<th
class=\\"\\"
colspan=\\"1\\"
>
@@ -107,138 +107,119 @@
class=\\"\\"
>
<td
class=\\"\\"
>
- firstName: tanner
- </td>
- <td
- class=\\"\\"
+ <span
+ style=\\"cursor: pointer;\\"
>
- lastName: linsley
- </td>
- <td
- class=\\"\\"
- >
- age: 29
- </td>
- <td
- class=\\"\\"
- >
- visits: 100
- </td>
- <td
- class=\\"\\"
- >
- status: In Relationship
+ 👉
+ </span>
+ lastName: linsley (2)
</td>
<td
class=\\"\\"
>
- progress: 50
- </td>
- </tr>
- <tr
- class=\\"\\"
- >
- <td
- class=\\"\\"
- >
- firstName: derek
- </td>
- <td
- class=\\"\\"
- >
- lastName: perkins
+ 2 Names
</td>
<td
class=\\"\\"
>
- age: 40
+ 27.5 (avg)
</td>
<td
class=\\"\\"
>
- visits: 40
+ 199 (total)
</td>
<td
class=\\"\\"
>
- status: Single
+ status: null
</td>
<td
class=\\"\\"
>
- progress: 80
+ 60 (med)
</td>
</tr>
<tr
class=\\"\\"
>
<td
class=\\"\\"
>
- firstName: joe
+ <span
+ style=\\"cursor: pointer;\\"
+ >
+ 👉
+ </span>
+ lastName: perkins (1)
</td>
<td
class=\\"\\"
>
- lastName: bergevin
+ 1 Names
</td>
<td
class=\\"\\"
>
- age: 45
+ 40 (avg)
</td>
<td
class=\\"\\"
>
- visits: 20
+ 40 (total)
</td>
<td
class=\\"\\"
>
- status: Complicated
+ status: null
</td>
<td
class=\\"\\"
>
- progress: 10
+ 80 (med)
</td>
</tr>
<tr
class=\\"\\"
>
<td
class=\\"\\"
>
- firstName: jaylen
+ <span
+ style=\\"cursor: pointer;\\"
+ >
+ 👉
+ </span>
+ lastName: bergevin (1)
</td>
<td
class=\\"\\"
>
- lastName: linsley
+ 1 Names
</td>
<td
class=\\"\\"
>
- age: 26
+ 45 (avg)
</td>
<td
class=\\"\\"
>
- visits: 99
+ 20 (total)
</td>
<td
class=\\"\\"
>
- status: In Relationship
+ status: null
</td>
<td
class=\\"\\"
>
- progress: 70
+ 10 (med)
</td>
</tr>
</tbody>
</table>
</DocumentFragment>"
`;
exports[`renders a filterable table 2`] = `
"Snapshot Diff:
- First value
+ Second value
@@ -6,17 +6,29 @@
<tr
class=\\"\\"
>
<th
class=\\"\\"
- colspan=\\"2\\"
+ colspan=\\"1\\"
>
Name
</th>
<th
class=\\"\\"
- colspan=\\"4\\"
+ colspan=\\"1\\"
+ >
+ Info
+ </th>
+ <th
+ class=\\"\\"
+ colspan=\\"1\\"
+ >
+ Name
+ </th>
+ <th
+ class=\\"\\"
+ colspan=\\"3\\"
>
Info
</th>
</tr>
<tr
@@ -42,13 +54,13 @@
<span
class=\\"\\"
style=\\"cursor: pointer;\\"
title=\\"Toggle GroupBy\\"
>
- 👊
+ 🛑
</span>
- First Name
+ Visits
</th>
<th
class=\\"\\"
colspan=\\"1\\"
>
@@ -57,11 +69,11 @@
style=\\"cursor: pointer;\\"
title=\\"Toggle GroupBy\\"
>
👊
</span>
- Age
+ First Name
</th>
<th
class=\\"\\"
colspan=\\"1\\"
>
@@ -70,11 +82,11 @@
style=\\"cursor: pointer;\\"
title=\\"Toggle GroupBy\\"
>
👊
</span>
- Visits
+ Age
</th>
<th
class=\\"\\"
colspan=\\"1\\"
>
@@ -114,10 +126,13 @@
>
👉
</span>
lastName: linsley (2)
</td>
+ <td
+ class=\\"\\"
+ />
<td
class=\\"\\"
>
2 Names
</td>
@@ -127,15 +142,10 @@
27.5 (avg)
</td>
<td
class=\\"\\"
>
- 199 (total)
- </td>
- <td
- class=\\"\\"
- >
status: null
</td>
<td
class=\\"\\"
>
@@ -155,22 +165,20 @@
</span>
lastName: perkins (1)
</td>
<td
class=\\"\\"
- >
- 1 Names
- </td>
+ />
<td
class=\\"\\"
>
- 40 (avg)
+ 1 Names
</td>
<td
class=\\"\\"
>
- 40 (total)
+ 40 (avg)
</td>
<td
class=\\"\\"
>
status: null
@@ -194,22 +202,20 @@
</span>
lastName: bergevin (1)
</td>
<td
class=\\"\\"
- >
- 1 Names
- </td>
+ />
<td
class=\\"\\"
>
- 45 (avg)
+ 1 Names
</td>
<td
class=\\"\\"
>
- 20 (total)
+ 45 (avg)
</td>
<td
class=\\"\\"
>
status: null"
`;

View File

@ -1,6 +1,5 @@
import '@testing-library/react/cleanup-after-each'
import '@testing-library/jest-dom/extend-expect'
// NOTE: jest-dom adds handy assertions to Jest and is recommended, but not required
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
@ -73,7 +72,7 @@ function Table({ columns, data }) {
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.render('Header')}
{column.render('Filter')}
{column.canFilter ? column.render('Filter') : null}
</th>
))}
</tr>
@ -139,19 +138,21 @@ function App() {
return <Table columns={columns} data={data} />
}
test('renders a sortable table', () => {
const { getByText, asFragment } = render(<App />)
test('renders a filterable table', () => {
const { getAllByPlaceholderText, asFragment } = render(<App />)
const beforeSort = asFragment()
const filterInputs = getAllByPlaceholderText('Search...')
fireEvent.click(getByText('First Name'))
const beforeFilter = asFragment()
const afterSort1 = asFragment()
fireEvent.change(filterInputs[1], { target: { value: 'l' } })
fireEvent.click(getByText('First Name'))
const afterFilter1 = asFragment()
const afterSort2 = asFragment()
fireEvent.change(filterInputs[1], { target: { value: 'er' } })
expect(beforeSort).toMatchDiffSnapshot(afterSort1)
expect(afterSort1).toMatchDiffSnapshot(afterSort2)
const afterFilter2 = asFragment()
expect(beforeFilter).toMatchDiffSnapshot(afterFilter1)
expect(afterFilter1).toMatchDiffSnapshot(afterFilter2)
})

View File

@ -0,0 +1,207 @@
import '@testing-library/react/cleanup-after-each'
import '@testing-library/jest-dom/extend-expect'
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import { useTable } from '../../hooks/useTable'
import { useGroupBy } from '../useGroupBy'
import { useExpanded } from '../useExpanded'
const data = [
{
firstName: 'tanner',
lastName: 'linsley',
age: 29,
visits: 100,
status: 'In Relationship',
progress: 50,
},
{
firstName: 'derek',
lastName: 'perkins',
age: 40,
visits: 40,
status: 'Single',
progress: 80,
},
{
firstName: 'joe',
lastName: 'bergevin',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
},
{
firstName: 'jaylen',
lastName: 'linsley',
age: 26,
visits: 99,
status: 'In Relationship',
progress: 70,
},
]
const defaultColumn = {
Cell: ({ value, column: { id } }) => `${id}: ${value}`,
Filter: ({ filterValue, setFilter }) => (
<input
value={filterValue || ''}
onChange={e => {
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder="Search..."
/>
),
}
function Table({ columns, data }) {
const { getTableProps, headerGroups, rows, prepareRow } = useTable(
{
columns,
data,
defaultColumn,
},
useGroupBy,
useExpanded
)
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>
{column.canGroupBy ? (
// If the column can be grouped, let's add a toggle
<span {...column.getGroupByToggleProps()}>
{column.grouped ? '🛑' : '👊'}
</span>
) : null}
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody>
{rows.map(
(row, i) =>
prepareRow(row) || (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.grouped ? (
<>
<span
style={{
cursor: 'pointer',
}}
onClick={() => row.toggleExpanded()}
>
{row.isExpanded ? '👇' : '👉'}
</span>
{cell.render('Cell')} ({row.subRows.length})
</>
) : cell.aggregated ? (
cell.render('Aggregated')
) : cell.repeatedValue ? null : (
cell.render('Cell')
)}
</td>
)
})}
</tr>
)
)}
</tbody>
</table>
)
}
function roundedMedian(values) {
let min = values[0] || ''
let max = values[0] || ''
values.forEach(value => {
min = Math.min(min, value)
max = Math.max(max, value)
})
return Math.round((min + max) / 2)
}
function App() {
const columns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
aggregate: ['sum', 'count'],
Aggregated: ({ value }) => `${value} Names`,
},
{
Header: 'Last Name',
accessor: 'lastName',
aggregate: ['sum', 'uniqueCount'],
Aggregated: ({ value }) => `${value} Unique Names`,
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'Age',
accessor: 'age',
aggregate: 'average',
Aggregated: ({ value }) => `${value} (avg)`,
},
{
Header: 'Visits',
accessor: 'visits',
aggregate: 'sum',
Aggregated: ({ value }) => `${value} (total)`,
},
{
Header: 'Status',
accessor: 'status',
},
{
Header: 'Profile Progress',
accessor: 'progress',
aggregate: roundedMedian,
Aggregated: ({ value }) => `${value} (med)`,
},
],
},
],
[]
)
return <Table columns={columns} data={data} />
}
test('renders a filterable table', () => {
const { getAllByText, asFragment } = render(<App />)
const groupByButtons = getAllByText('👊')
const beforeGrouping = asFragment()
fireEvent.click(groupByButtons[1])
const afterGrouping1 = asFragment()
fireEvent.click(groupByButtons[3])
const afterGrouping2 = asFragment()
expect(beforeGrouping).toMatchDiffSnapshot(afterGrouping1)
expect(afterGrouping1).toMatchDiffSnapshot(afterGrouping2)
})

View File

@ -1,6 +1,5 @@
import '@testing-library/react/cleanup-after-each'
import '@testing-library/jest-dom/extend-expect'
// NOTE: jest-dom adds handy assertions to Jest and is recommended, but not required
import React from 'react'
import { render, fireEvent } from '@testing-library/react'

View File

@ -181,14 +181,6 @@ function useMain(instance) {
}
})
// then filter any rows without subcolumns because it would be strange to show
filteredRows = filteredRows.filter(row => {
if (!row.subRows) {
return true
}
return row.subRows.length > 0
})
return filteredRows
}

View File

@ -38,6 +38,10 @@ const propTypes = {
export const useGroupBy = hooks => {
hooks.columnsBeforeHeaderGroups.push(columnsBeforeHeaderGroups)
hooks.columnsBeforeHeaderGroupsDeps.push((deps, instance) => {
deps.push(instance.state[0].groupBy)
return deps
})
hooks.useMain.push(useMain)
}