Skip to content

feat: allow text matching with a regex #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ expect(getRowByFirstCellText('John Smith')).toBeVisible()
fireEvent.click(within(getRowByFirstCellText('John Smith')).getByText('Delete'))
```

Users will generally find rows by scanning the content in the first column, then reading across the row. This finds that row (rather than just the first cell), which can then be used to identify other items within that row.
Users will generally find rows by scanning the content in the first column, then reading across the row. This finds that row (rather than just the first cell), which can then be used to identify other items within that row. You can also use regular expression matching instead of looking for an exact text:

```js
expect(getRowByFirstCellText(/John Smith/)).toBeVisible()
```

### Column cells by header text

Expand All @@ -125,7 +129,7 @@ ageCells.forEach((cell, index) => {
})
```

Returns an array of cells based on the text in the column header. Note that there is no DOM 'column' element, so it is an array of cells. If multiple columns have the same header text, the first is used. Optionally, this also supports an index (starting from zero) to support having multiple header rows:
Returns an array of cells based on the text (or a regex) in the column header. Note that there is no DOM 'column' element, so it is an array of cells. If multiple columns have the same header text, the first is used. Optionally, this also supports an index (starting from zero) to support having multiple header rows:

```js
const { getAllColumnCellsByHeaderText } = render(<MyTable />)
Expand All @@ -141,7 +145,7 @@ expect(getCellByRowAndColumnHeaders('John Smith', 'Age')).toHaveTextContent(
)
```

If a user is trying to find a specific value for a specific entity, they might scan from the row and column headers. This finds cells based on those headers. Like column cells by header text, it only uses the first column with the specified header text (but will handle multiple rows), and supports a header index.
If a user is trying to find a specific value for a specific entity, they might scan from the row and column headers. This finds cells based on those headers. Like column cells by header text (or regex), it only uses the first column with the specified header text (or regex) (but will handle multiple rows), and supports a header index.

## Examples

Expand All @@ -150,7 +154,6 @@ See [example tests](./example/src/SimpleTable.test.js)
## Future changes

- Address the first column limitation
- Allow custom text normalisation/matching
- Allow Nth cell in a row, rather than just first

## Development
Expand Down
17 changes: 9 additions & 8 deletions src/cellByRowAndColumnHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { nthHeaderError } from './utils/nthHeaderError'

function queryAllCellsByRowAndColumnHeaders(
container: HTMLElement,
rowHeaderText: string,
columnheaderText: string,
rowHeaderTextQuery: string | RegExp,
columnheaderTextQuery: string | RegExp,
headerRowIndex = 0
) {
const rows = queryAllRowsByFirstCellText(container, rowHeaderText)
const rows = queryAllRowsByFirstCellText(container, rowHeaderTextQuery)

const columnIndex = getColumnIndexByHeaderText(
container,
columnheaderText,
columnheaderTextQuery,
headerRowIndex
)

Expand All @@ -25,17 +25,18 @@ function queryAllCellsByRowAndColumnHeaders(

const getMultipleError = (
_c: Element | null,
rowHeaderText: string,
columnheaderText: string,
rowHeaderText: string | RegExp,
columnheaderText: string | RegExp,
headerRowIndex = 0
) =>
`Found multiple cells with ${rowHeaderText} in the first column and ${columnheaderText} in the ${nthHeaderError(
headerRowIndex
)}`

const getMissingError = (
_c: Element | null,
rowHeaderText: string,
columnheaderText: string,
rowHeaderText: string | RegExp,
columnheaderText: string | RegExp,
headerRowIndex = 0
) =>
`Found no rows with ${rowHeaderText} in the first column and ${columnheaderText} in the ${nthHeaderError(
Expand Down
31 changes: 23 additions & 8 deletions src/columnCellsByHeaderText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { nthHeaderError } from './utils/nthHeaderError'

function queryAllColumnCellsByHeaderText(
container: HTMLElement,
textContent: string,
textQuery: string | RegExp,
headerRowIndex = 0
) {
const cellIndex = getColumnIndexByHeaderText(
container,
textContent,
textQuery,
headerRowIndex
)

Expand All @@ -24,18 +24,33 @@ function queryAllColumnCellsByHeaderText(

const getMultipleError = (
_c: Element | null,
textContent: string,
textQuery: string | RegExp,
headerRowIndex = 0
) =>
`Found multiple cells with ${textContent} in the ${nthHeaderError(
) => {
if (typeof textQuery === 'string') {
return `Found multiple cells with ${textQuery} in the ${nthHeaderError(
headerRowIndex
)}`
}
return `Found multiple cells matching ${textQuery} in the ${nthHeaderError(
headerRowIndex
)}`
}

const getMissingError = (
_c: Element | null,
textContent: string,
textQuery: string | RegExp,
headerRowIndex = 0
) =>
`Found no rows with ${textContent} in the ${nthHeaderError(headerRowIndex)}`
) => {
if (typeof textQuery === 'string') {
return `Found no rows with ${textQuery} in the ${nthHeaderError(
headerRowIndex
)}`
}
return `Found no rows matching ${textQuery} in the ${nthHeaderError(
headerRowIndex
)}`
}

const [
queryColumnCellByHeaderText,
Expand Down
229 changes: 163 additions & 66 deletions src/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,77 +206,174 @@ Ignored nodes: comments, script, style
expect(queries.getAllCells(container)).toHaveLength(48)
})

it('should find cells by row and column headings', () => {
const container = render(simpleTable)
expect(
queries.getCellByRowAndColumnHeaders(container, 'trouble', 'Status').id
).toEqual('body-cell-29')
expect(
queries.getCellByRowAndColumnHeaders(container, 'reason', 'Age').id
).toEqual('body-cell-21')
expect(
queries.queryCellByRowAndColumnHeaders(container, 'NOT A ROW', 'Status')
).toBeNull()
expect(
queries.queryCellByRowAndColumnHeaders(
describe('should find cells by row and column headings', () => {
it('using exact test match', () => {
const container = render(simpleTable)
expect(
queries.getCellByRowAndColumnHeaders(container, 'trouble', 'Status').id
).toEqual('body-cell-29')
expect(
queries.getCellByRowAndColumnHeaders(container, 'reason', 'Age').id
).toEqual('body-cell-21')
expect(
queries.queryCellByRowAndColumnHeaders(container, 'NOT A ROW', 'Status')
).toBeNull()
expect(
queries.queryCellByRowAndColumnHeaders(
container,
'trouble',
'Not a column'
)
).toBeNull()
})
it('using a regex', () => {
const container = render(simpleTable)
expect(
queries.getCellByRowAndColumnHeaders(container, /trouble/, /Status/).id
).toEqual('body-cell-29')
expect(
queries.getCellByRowAndColumnHeaders(container, /reason/, /Age/).id
).toEqual('body-cell-21')
expect(
queries.queryCellByRowAndColumnHeaders(container, /NOT A ROW/, /Status/)
).toBeNull()
expect(
queries.queryCellByRowAndColumnHeaders(
container,
/trouble/,
/Not a column/
)
).toBeNull()
expect(
queries.queryCellByRowAndColumnHeaders(container, /.*/, /Not a column/)
).toBeNull()
expect(
queries.queryAllCellsByRowAndColumnHeaders(container, /.*/, /Age/)
).toHaveLength(8)
expect(
queries.queryAllCellsByRowAndColumnHeaders(container, /reason/, /.*/)
).toHaveLength(1)
expect(
queries.queryAllCellsByRowAndColumnHeaders(container, /.*/, /.*/)
).toHaveLength(8)
})
})

describe('should find column cells by header text', () => {
it('using exact test match', () => {
const container = render(simpleTable)
expect(
queries.queryAllColumnCellsByHeaderText(container, 'NOT A COLUMN')
).toHaveLength(0)
const ageCells = queries.getAllColumnCellsByHeaderText(container, 'Age')
expect(ageCells).toHaveLength(8)
expect(ageCells.map((cell) => cell.id)).toEqual([
'header-cell-3',
'body-cell-3',
'body-cell-9',
'body-cell-15',
'body-cell-21',
'body-cell-27',
'body-cell-33',
'body-cell-39'
])
const statusCells = queries.getAllColumnCellsByHeaderText(
container,
'trouble',
'Not a column'
/Status/
)
).toBeNull()
})
expect(statusCells).toHaveLength(8)
expect(statusCells.map((cell) => cell.id)).toEqual([
'header-cell-5',
'body-cell-5',
'body-cell-11',
'body-cell-17',
'body-cell-23',
'body-cell-29',
'body-cell-35',
'body-cell-41'
])
})

it('should find column cells by header text', () => {
const container = render(simpleTable)
expect(
queries.queryAllColumnCellsByHeaderText(container, 'NOT A COLUMN')
).toHaveLength(0)
const ageCells = queries.getAllColumnCellsByHeaderText(container, 'Age')
expect(ageCells).toHaveLength(8)
expect(ageCells.map((cell) => cell.id)).toEqual([
'header-cell-3',
'body-cell-3',
'body-cell-9',
'body-cell-15',
'body-cell-21',
'body-cell-27',
'body-cell-33',
'body-cell-39'
])
const statusCells = queries.getAllColumnCellsByHeaderText(
container,
'Status'
)
expect(statusCells).toHaveLength(8)
expect(statusCells.map((cell) => cell.id)).toEqual([
'header-cell-5',
'body-cell-5',
'body-cell-11',
'body-cell-17',
'body-cell-23',
'body-cell-29',
'body-cell-35',
'body-cell-41'
])
it('using a regex match', () => {
const container = render(simpleTable)
expect(
queries.queryAllColumnCellsByHeaderText(container, /NOT A COLUMN/)
).toHaveLength(0)
expect(
queries.queryAllColumnCellsByHeaderText(container, /.*/)
).toHaveLength(8)
const ageCells = queries.getAllColumnCellsByHeaderText(container, /Age/)
expect(ageCells).toHaveLength(8)
expect(ageCells.map((cell) => cell.id)).toEqual([
'header-cell-3',
'body-cell-3',
'body-cell-9',
'body-cell-15',
'body-cell-21',
'body-cell-27',
'body-cell-33',
'body-cell-39'
])
const statusCells = queries.getAllColumnCellsByHeaderText(
container,
/Status/
)
expect(statusCells).toHaveLength(8)
expect(statusCells.map((cell) => cell.id)).toEqual([
'header-cell-5',
'body-cell-5',
'body-cell-11',
'body-cell-17',
'body-cell-23',
'body-cell-29',
'body-cell-35',
'body-cell-41'
])
})
})

it('should find rows by the first cell text', () => {
const container = render(simpleTable)
expect(
queries.queryAllRowsByFirstCellText(container, 'NOT A ROW')
).toHaveLength(0)
expect(queries.getAllRowsByFirstCellText(container, 'reason')).toHaveLength(
1
)
expect(queries.getRowByFirstCellText(container, 'reason').id).toEqual(
'body-row-4'
)
expect(queries.getRowByFirstCellText(container, 'First Name').id).toEqual(
'header-row'
)
expect(queries.getRowByFirstCellText(container, 'midnight').id).toEqual(
'body-row-2'
)
describe('should find rows by the first cell text', () => {
it('using exact text match', () => {
const container = render(simpleTable)
expect(
queries.queryAllRowsByFirstCellText(container, 'NOT A ROW')
).toHaveLength(0)
expect(
queries.getAllRowsByFirstCellText(container, 'reason')
).toHaveLength(1)
expect(queries.getRowByFirstCellText(container, 'reason').id).toEqual(
'body-row-4'
)
expect(queries.getRowByFirstCellText(container, 'First Name').id).toEqual(
'header-row'
)
expect(queries.getRowByFirstCellText(container, 'midnight').id).toEqual(
'body-row-2'
)
})

it('using a regex', () => {
const container = render(simpleTable)
expect(
queries.queryAllRowsByFirstCellText(
container,
/this-regex-has-no-match/
)
).toHaveLength(0)
expect(
queries.getAllRowsByFirstCellText(container, /reas*/)
).toHaveLength(1)
expect(queries.getAllRowsByFirstCellText(container, /.*/)).toHaveLength(8)
expect(queries.getRowByFirstCellText(container, 'reason').id).toEqual(
'body-row-4'
)
expect(queries.getRowByFirstCellText(container, 'First Name').id).toEqual(
'header-row'
)
expect(queries.getRowByFirstCellText(container, 'midnight').id).toEqual(
'body-row-2'
)
})
})

it('should find rowgroups', () => {
Expand Down
Loading