From 4519be92d6ed2913c2c51eba0fd3b4890aafeac1 Mon Sep 17 00:00:00 2001 From: Greg Lin Date: Fri, 26 May 2023 18:36:42 -0500 Subject: [PATCH] Add JavaScript API methods to change page and set page size --- NEWS.md | 7 +++ srcjs/Reactable.js | 8 +++ srcjs/__tests__/Reactable.test.js | 81 ++++++++++++++++++++++++++++++- vignettes/javascript-api.Rmd | 58 ++++++++++++++++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0357e0b2..ecac8e10 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,12 @@ # reactable 0.4.4.9000 (Unreleased) +## New features + +* New [`Reactable.gotoPage()`](https://glin.github.io/reactable/articles/javascript-api.html#reactable-gotopage) + and [`Reactable.setPageSize()`](https://glin.github.io/reactable/articles/javascript-api.html#reactable-setpagesize) + methods in the JavaScript API to change the current page or set the current page size. + ([#322](https://github.com/glin/reactable/issues/322)) + # reactable 0.4.4 [Documentation - reactable 0.4.4](https://v0-4-4--reactable-docs.netlify.app/) diff --git a/srcjs/Reactable.js b/srcjs/Reactable.js index 27b23200..4c8768ea 100644 --- a/srcjs/Reactable.js +++ b/srcjs/Reactable.js @@ -114,6 +114,14 @@ export function onStateChange(tableId, listenerFn) { return getInstance(tableId).onStateChange(listenerFn) } +export function gotoPage(tableId, pageIndex) { + getInstance(tableId).gotoPage(pageIndex) +} + +export function setPageSize(tableId, pageSize) { + getInstance(tableId).setPageSize(pageSize) +} + export default function Reactable({ data, columns, diff --git a/srcjs/__tests__/Reactable.test.js b/srcjs/__tests__/Reactable.test.js index eca0c4c4..45c3e926 100644 --- a/srcjs/__tests__/Reactable.test.js +++ b/srcjs/__tests__/Reactable.test.js @@ -21,6 +21,19 @@ afterEach(() => jest.clearAllMocks()) expect.extend(matchers) +// expect.toThrow() doesn't suppress console errors, so this can be used to test errors instead. +// https://github.com/jestjs/jest/issues/5785 +const expectToThrow = func => { + // Even though the error is caught, it still gets printed to the console + // so we mock that out to avoid the wall of red text. + jest.spyOn(console, 'error') + console.error.mockImplementation(() => {}) + + expect(func).toThrow() + + console.error.mockRestore() +} + const getRoot = container => container.querySelector('.Reactable.ReactTable') const getTable = container => container.querySelector('.rt-table') const getThead = container => container.querySelector('.rt-thead') @@ -9819,8 +9832,12 @@ describe('reactable JavaScript API', () => { expect(queryByText('col-a')).toBeVisible() expect(queryByText('col-c')).toBeVisible() - act(() => reactable.setHiddenColumns('my-tbl', prevHiddenColumns => prevHiddenColumns.concat('b'))) - act(() => reactable.setHiddenColumns('my-tbl', prevHiddenColumns => prevHiddenColumns.concat('c'))) + act(() => + reactable.setHiddenColumns('my-tbl', prevHiddenColumns => prevHiddenColumns.concat('b')) + ) + act(() => + reactable.setHiddenColumns('my-tbl', prevHiddenColumns => prevHiddenColumns.concat('c')) + ) expect(reactable.getState('my-tbl').hiddenColumns).toEqual(['b', 'c']) expect(queryByText('col-b')).toEqual(null) expect(queryByText('col-c')).toEqual(null) @@ -9936,4 +9953,64 @@ describe('reactable JavaScript API', () => { await waitFor(() => expect(currentState.searchValue).toEqual('aaa')) await waitFor(() => expect(currentState.groupBy).toEqual(['a'])) }) + + it('Reactable.gotoPage', () => { + const props = { + data: { a: ['a1', 'a2', 'a3', 'a4', 'a5', 'a6'] }, + columns: [{ name: 'a', id: 'a' }], + defaultPageSize: 2, + elementId: 'my-tbl' + } + const { queryByText } = render() + + expect(reactable.getState('my-tbl').pageIndex).toEqual(0) + + act(() => reactable.gotoPage('my-tbl', 1)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(1) + expect(queryByText('a1')).toEqual(null) + expect(queryByText('a3')).toBeVisible() + + act(() => reactable.gotoPage('my-tbl', 0)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(0) + expect(queryByText('a1')).toBeVisible() + expect(queryByText('a3')).toEqual(null) + + // Should ignore page indexes beyond range + act(() => reactable.gotoPage('my-tbl', 1)) + act(() => reactable.gotoPage('my-tbl', -1)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(1) + act(() => reactable.gotoPage('my-tbl', 999)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(1) + + // Should accept a callback + act(() => reactable.gotoPage('my-tbl', currentIndex => currentIndex + 1)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(2) + act(() => reactable.gotoPage('my-tbl', currentIndex => currentIndex - 1)) + expect(reactable.getState('my-tbl').pageIndex).toEqual(1) + }) +}) + +it('Reactable.setPageSize', () => { + const props = { + data: { a: ['a1', 'a2', 'a3', 'a4', 'a5', 'a6'] }, + columns: [{ name: 'a', id: 'a' }], + defaultPageSize: 2, + elementId: 'my-tbl' + } + const { queryByText } = render() + + expect(reactable.getState('my-tbl').pageSize).toEqual(2) + + act(() => reactable.setPageSize('my-tbl', 1)) + expect(reactable.getState('my-tbl').pageSize).toEqual(1) + expect(queryByText('a2')).toEqual(null) + + act(() => reactable.setPageSize('my-tbl', 6)) + expect(reactable.getState('my-tbl').pageSize).toEqual(6) + + act(() => reactable.setPageSize('my-tbl', 999)) + expect(reactable.getState('my-tbl').pageSize).toEqual(999) + + // Invalid values should err + expectToThrow(() => act(() => reactable.setPageSize('my-tbl', -1))) }) diff --git a/vignettes/javascript-api.Rmd b/vignettes/javascript-api.Rmd index 4ebe6b11..bb8709c8 100644 --- a/vignettes/javascript-api.Rmd +++ b/vignettes/javascript-api.Rmd @@ -675,6 +675,64 @@ server <- function(input, output) { shinyApp(ui, server) ``` +### `Reactable.gotoPage()` + +::: {.callout} +New in v0.4.4.9000 +::: + +Changes the current page. `pageIndex` can either be a number or a function that takes +the previous `pageIndex` and returns the new value. + +`pageIndex` is zero-based, so the first page would have a `pageIndex` of `0`. +If `pageIndex` is outside the valid `pageIndex` range, `Reactable.gotoPage()` will do nothing. + +```ts +Reactable.gotoPage( + tableId: string, + pageIndex?: number | Function +) +``` + +#### Examples + +```js +// Go to page index 0 (the first page) +Reactable.gotoPage('cars-table', 0) + +// Go to page index 2 (the third page) +Reactable.gotoPage('cars-table', 2) + +// Go to the next page +Reactable.gotoPage('cars-table', prevPage => prevPage + 1) + +// Go to the previous page +Reactable.gotoPage('cars-table', prevPage => prevPage - 1) +``` + +### `Reactable.setPageSize()` + +::: {.callout} +New in v0.4.4.9000 +::: + +Sets the current page size. + +```ts +Reactable.gotoPage( + tableId: string, + pageSize?: number +) +``` + +#### Examples + +```js +// Set the page size to 10 +Reactable.setPageSize('cars-table', 10) +``` + + ```{css echo=FALSE} /* rmarkdown html documents */ .main-container {