From deeb4bf5a90a3b475b8db2c81c968c3e160c5401 Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Mon, 20 May 2024 15:03:04 +0200 Subject: [PATCH 1/4] feat(table): Display a simple table in DataView --- .../data-view/examples/DataView/DataView.md | 3 +- .../DataViewPredefinedLayoutExample.tsx | 75 ++++++++++++++----- .../module/src/DataView/DataView.test.tsx | 13 ---- packages/module/src/Hooks/index.ts | 2 +- packages/module/src/Hooks/pagination.ts | 28 +++++++ .../module/src/Hooks/useDataViewPagination.ts | 4 - 6 files changed, 89 insertions(+), 36 deletions(-) delete mode 100644 packages/module/src/DataView/DataView.test.tsx create mode 100644 packages/module/src/Hooks/pagination.ts delete mode 100644 packages/module/src/Hooks/useDataViewPagination.ts diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md index 51a960ba..ebe1b670 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md @@ -13,7 +13,8 @@ source: react propComponents: ['DataView'] sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataView.md --- -import { Pagination } from '@patternfly/react-core'; +import { useMemo } from 'react'; +import { useDataViewPagination } from '@patternfly/react-data-view/dist/dynamic/Hooks'; import DataView from '@patternfly/react-data-view/dist/dynamic/DataView'; import DataViewToolbar from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataViewPredefinedLayoutExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataViewPredefinedLayoutExample.tsx index 6a6badd3..c6ace0dd 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataViewPredefinedLayoutExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/DataView/DataViewPredefinedLayoutExample.tsx @@ -1,24 +1,65 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Pagination } from '@patternfly/react-core'; +import { Table, Tbody, Th, Thead, Tr, Td } from '@patternfly/react-table'; +import { useDataViewPagination } from '@patternfly/react-data-view/dist/dynamic/Hooks'; import DataView from '@patternfly/react-data-view/dist/dynamic/DataView'; import DataViewToolbar from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; -const layoutItemStyling = { - width: '100%', - height: '5rem', - padding: 'var(--pf-t--global--spacer--md)', - border: 'var(--pf-t--global--border--width--box--default) dashed var(--pf-t--global--border--color--default)' -}; +const perPageOptions = [ + { title: '5', value: 5 }, + { title: '10', value: 10 } +] -const PAGINATION = { - page: 1, - perPage: 1 +interface Repository { + name: string; + branches: string | null; + prs: string | null; + workspaces: string; + lastCommit: string; } -export const BasicExample: React.FunctionComponent = () => ( - - } /> -
Data representation
- } /> -
-) +const repositories: Repository[] = [ + { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' }, + { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' }, + { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }, + { name: 'one - 4', branches: 'two - 4', prs: 'null', workspaces: 'four - 4', lastCommit: 'five - 4' }, + { name: 'one - 5', branches: 'two - 5', prs: 'three - 5', workspaces: 'four - 5', lastCommit: 'five - 5' }, + { name: 'one - 6', branches: 'two - 6', prs: 'three - 6', workspaces: 'four - 6', lastCommit: 'five - 6' } +]; + +const cols = { + name: 'Repositories', + branches: 'Branches', + prs: 'Pull requests', + workspaces: 'Workspaces', + lastCommit: 'Last commit' +}; + +const ouiaId = 'LayoutExample'; + +export const BasicExample: React.FunctionComponent = () => { + const pagination = useDataViewPagination({ perPage: 5 }); + const { page, perPage } = pagination; + + const data = useMemo(() => repositories.slice((page - 1) * perPage, ((page - 1) * perPage) + perPage), [ page, perPage ]); + + return ( + + } /> + + + + {Object.values(cols).map((column, index) => )} + + + + {data.map((repo, rowIndex) => ( + + {Object.keys(cols).map((column, colIndex) => )} + + ))} + +
{column}
{repo[column]}
+ } /> +
+ )} diff --git a/packages/module/src/DataView/DataView.test.tsx b/packages/module/src/DataView/DataView.test.tsx deleted file mode 100644 index afa5f8be..00000000 --- a/packages/module/src/DataView/DataView.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom' -import DataView from './DataView'; - -describe('DataView', () => { - - it('should render data view', () => { - render(<>Data view content); - expect(screen.getByText('Data view content')).toBeInTheDocument(); - }); - -}); diff --git a/packages/module/src/Hooks/index.ts b/packages/module/src/Hooks/index.ts index 6929c3b3..cb727655 100644 --- a/packages/module/src/Hooks/index.ts +++ b/packages/module/src/Hooks/index.ts @@ -1 +1 @@ -export * from './useDataViewPagination'; +export * from './pagination'; diff --git a/packages/module/src/Hooks/pagination.ts b/packages/module/src/Hooks/pagination.ts new file mode 100644 index 00000000..f37162e8 --- /dev/null +++ b/packages/module/src/Hooks/pagination.ts @@ -0,0 +1,28 @@ +import { useState } from "react"; + +export interface UseDataViewPaginationProps { + page?: number; + perPage: number; +} + +export interface DataViewPaginationProps extends UseDataViewPaginationProps { + page: number; +} + +export const useDataViewPagination = ({ page = 1, perPage }: UseDataViewPaginationProps) => { + const [ state, setState ] = useState({ page, perPage }); + + const onPerPageSelect = (_event: React.MouseEvent | React.KeyboardEvent | MouseEvent | undefined, newPerPage: number) => { + setState(prev => ({ ...prev, perPage: newPerPage })); + } + + const onSetPage = (_event: React.MouseEvent | React.KeyboardEvent | MouseEvent | undefined, newPage: number) => { + setState(prev => ({ ...prev, page: newPage })); + } + + return { + ...state, + onPerPageSelect, + onSetPage + } +} diff --git a/packages/module/src/Hooks/useDataViewPagination.ts b/packages/module/src/Hooks/useDataViewPagination.ts deleted file mode 100644 index fe1c3c56..00000000 --- a/packages/module/src/Hooks/useDataViewPagination.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface DataViewPaginationProps { - page: number; - perPage: number; -} \ No newline at end of file From 0697b940ed50acc02c97f7549e094312749b53ac Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Mon, 20 May 2024 15:03:25 +0200 Subject: [PATCH 2/4] Test useDataViewPagination hook --- packages/module/src/Hooks/pagination.test.tsx | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 packages/module/src/Hooks/pagination.test.tsx diff --git a/packages/module/src/Hooks/pagination.test.tsx b/packages/module/src/Hooks/pagination.test.tsx new file mode 100644 index 00000000..6a1b03ae --- /dev/null +++ b/packages/module/src/Hooks/pagination.test.tsx @@ -0,0 +1,57 @@ +import '@testing-library/jest-dom'; +import { renderHook, act } from '@testing-library/react'; +import { useDataViewPagination } from './pagination'; + +describe('useDataViewPagination', () => { + + it('should get initial state correctly - no page', () => { + const { result } = renderHook(() => useDataViewPagination({ perPage: 7 })) + expect(result.current).toEqual({ + onPerPageSelect: expect.any(Function), + onSetPage: expect.any(Function), + page: 1, + perPage: 7 + }) + }); + + it('should get initial state correctly - page set', () => { + const { result } = renderHook(() => useDataViewPagination({ page: 3, perPage: 5 })) + expect(result.current).toEqual({ + onPerPageSelect: expect.any(Function), + onSetPage: expect.any(Function), + page: 3, + perPage: 5 + }) + }); + + it('should set page correctly', () => { + const { result } = renderHook(() => useDataViewPagination({ page: 3, perPage: 5 })) + + act(() => { + result.current.onSetPage(undefined, 8); + }); + + expect(result.current).toEqual({ + onPerPageSelect: expect.any(Function), + onSetPage: expect.any(Function), + page: 8, + perPage: 5 + }) + }); + + it('should set perPage correctly', () => { + const { result } = renderHook(() => useDataViewPagination({ page: 3, perPage: 5 })) + + act(() => { + result.current.onPerPageSelect(undefined, 50); + }); + + expect(result.current).toEqual({ + onPerPageSelect: expect.any(Function), + onSetPage: expect.any(Function), + page: 3, + perPage: 50 + }) + }); + +}); From 484d5bd5f207da44bb6250ecb100142060f11450 Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Mon, 20 May 2024 15:03:52 +0200 Subject: [PATCH 3/4] Test table rendering via Cypress --- cypress/component/DataView.cy.tsx | 65 +++++++++++++++++++++++++++++-- cypress/e2e/DataView.spec.cy.ts | 25 +++++++++++- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/cypress/component/DataView.cy.tsx b/cypress/component/DataView.cy.tsx index 7199a711..14ad546d 100644 --- a/cypress/component/DataView.cy.tsx +++ b/cypress/component/DataView.cy.tsx @@ -1,29 +1,86 @@ import React from 'react'; import { Pagination } from '@patternfly/react-core'; +import { Table, Tbody, Th, Thead, Tr, Td } from '@patternfly/react-table'; import DataView from '../../packages/module/dist/dynamic/DataView'; import DataViewToolbar from '../../packages/module/dist/esm/DataViewToolbar'; +interface Repository { + name: string; + branches: string | null; + prs: string | null; + workspaces: string; + lastCommit: string; +} + const PAGINATION = { page: 1, - perPage: 1 + perPage: 10 } +const repositories: Repository[] = [ + { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' }, + { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' }, + { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }, + { name: 'one - 4', branches: 'two - 4', prs: 'null', workspaces: 'four - 4', lastCommit: 'five - 4' }, + { name: 'one - 5', branches: 'two - 5', prs: 'three - 5', workspaces: 'four - 5', lastCommit: 'five - 5' }, + { name: 'one - 6', branches: 'two - 6', prs: 'three - 6', workspaces: 'four - 6', lastCommit: 'five - 6' } +]; + +const cols: Record = { + name: 'Repositories', + branches: 'Branches', + prs: 'Pull requests', + workspaces: 'Workspaces', + lastCommit: 'Last commit' +}; + describe('DataView', () => { it('renders the data view layout', () => { cy.mount(<>Data view content) cy.get('[data-ouia-component-id="DataView-stack-item-0"]').contains('Data view content'); }); - it('renders the data view with toolbar, data section and footer', () => { + it('renders the data view with toolbar, tabular data section and footer', () => { + const ouiaId = 'data'; + cy.mount( } /> - <>Data section + + + + {Object.values(cols).map((column, index) => )} + + + + {repositories.map((repo, rowIndex) => ( + + + + + + + + ))} + +
{column}
{repo.name}{repo.branches}{repo.prs}{repo.workspaces}{repo.lastCommit}
} ouiaId="DataViewFooter" />
); cy.get('[data-ouia-component-id="DataViewToolbar-pagination"]').should('exist'); - cy.get('[data-ouia-component-id="DataView-stack-item-1"]').contains('Data section'); + + cy.get('[data-ouia-component-id="data-th-0"]').contains('Repositories'); + cy.get('[data-ouia-component-id="data-th-1"]').contains('Branches'); + cy.get('[data-ouia-component-id="data-th-2"]').contains('Pull requests'); + cy.get('[data-ouia-component-id="data-th-3"]').contains('Workspaces'); + cy.get('[data-ouia-component-id="data-th-4"]').contains('Last commit'); + + cy.get('[data-ouia-component-id="data-td-0-name"]').contains('one'); + cy.get('[data-ouia-component-id="data-td-2-branches"]').contains('two - 3'); + cy.get('[data-ouia-component-id="data-td-3-prs"]').contains('null'); + cy.get('[data-ouia-component-id="data-td-4-workspaces"]').contains('four - 5'); + cy.get('[data-ouia-component-id="data-td-5-last-commit"]').contains('five - 6'); + cy.get('[data-ouia-component-id="DataViewFooter-pagination"]').should('exist'); }); }); \ No newline at end of file diff --git a/cypress/e2e/DataView.spec.cy.ts b/cypress/e2e/DataView.spec.cy.ts index b3d42025..e32933a0 100644 --- a/cypress/e2e/DataView.spec.cy.ts +++ b/cypress/e2e/DataView.spec.cy.ts @@ -1,6 +1,27 @@ describe('Test the Data view docs page', () => { - it.skip('renders the docs', () => { - cy.visit('http://localhost:8006/extensions/data-view/data-view'); + it('displays a layout with a table and paginates', () => { + const ouiaId = 'LayoutExample'; + + cy.visit('http://localhost:8006/extensions/data-view/data-view-layout'); + + cy.get(`[data-ouia-component-id="${ouiaId}Header-pagination"]`).should('exist'); + + cy.get(`[data-ouia-component-id="${ouiaId}Footer-pagination"]`).should('exist'); + + cy.get(`[data-ouia-component-id="${ouiaId}-th-0"]`).contains('Repositories'); + cy.get(`[data-ouia-component-id="${ouiaId}-th-4"]`).contains('Last commit'); + + cy.get(`[data-ouia-component-id="${ouiaId}-td-0-0"]`).contains('one'); + cy.get(`[data-ouia-component-id="${ouiaId}-td-4-4"]`).contains('five - 5'); + cy.get(`[data-ouia-component-id="${ouiaId}-td-5-4"]`).should('not.exist'); + + // move to the next page + cy.get(`[data-action="next"`).first().click({ force: true }); + cy.get(`[data-ouia-component-id="${ouiaId}-td-0-4"]`).contains('five - 6'); + + // move to previous page + cy.get(`[data-action="previous"`).eq(1).click({ force: true }); + cy.get(`[data-ouia-component-id="${ouiaId}-td-0-4"]`).contains('five'); }) }) \ No newline at end of file From b724159f8f3a64355896ca9b412ec8b0d1d4fa00 Mon Sep 17 00:00:00 2001 From: Filip Hlavac Date: Mon, 20 May 2024 17:54:57 +0200 Subject: [PATCH 4/4] Update the hooks docs --- .../content/extensions/data-view/examples/Hooks/Hooks.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Hooks/Hooks.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Hooks/Hooks.md index b81e0017..508b6913 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Hooks/Hooks.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Hooks/Hooks.md @@ -17,7 +17,4 @@ Data view hooks provide a predefined solution to manage state of the data view. ### useDataViewPagination() -The `useDataViewPagination` hook manages the pagination state of the data view. It retrieves the current `page` and `perPage` values together with functions to set them (`setPage`, `setPerPage`) - -Coming soon... - +The `useDataViewPagination` hook manages the pagination state of the data view. It retrieves the current `page` and `perPage` values together with functions to set them (`onSetPage`, `onPerPageSelect`). You can easily spread the retrieved values to the PatternFly [pagination](/components/pagination) component and make it live.