diff --git a/cypress/component/SkeletonTable.cy.tsx b/cypress/component/SkeletonTable.cy.tsx index 35ef7d1e..8b8588a4 100644 --- a/cypress/component/SkeletonTable.cy.tsx +++ b/cypress/component/SkeletonTable.cy.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Th } from '@patternfly/react-table'; -import SkeletonTable from '../../packages/module/dist/dynamic/SkeletonTable'; +import { RowSelectVariant } from '@patternfly/react-table'; +import SkeletonTable from '@patternfly/react-component-groups/dist/dynamic/SkeletonTable'; describe('SkeletonTable', () => { beforeEach(() => { @@ -8,59 +8,66 @@ describe('SkeletonTable', () => { }); it('renders SkeletonTable', () => { - const SkeletonTableExample = ; + const SkeletonTableExample = ; cy.mount(SkeletonTableExample); cy.get('table').should('exist'); - cy.get('table thead tr').should('have.text', 'firstsecond'); + cy.get('table thead tr th').eq(0).should('have.text', 'First'); + cy.get('table thead tr th').eq(1).should('have.text', 'Second'); }); it ('can be used without passing columns', () => { - const SkeletonTableExample = ; + const SkeletonTableExample = ; cy.mount(SkeletonTableExample); cy.get('table').should('exist'); cy.get('table thead tr').should('have.text', ''); }); it('contains checkboxes when passed isSelectable', () => { - const SkeletonTableExample = ; + const SkeletonTableExample = ; cy.mount(SkeletonTableExample); cy.get('table').should('exist'); - cy.get('table thead tr').should('have.text', 'firstsecond'); + cy.get('table thead tr th').eq(0).should('have.text', 'Data selection table header cell'); + cy.get('table thead tr th').eq(1).should('have.text', 'First'); + cy.get('table thead tr th').eq(2).should('have.text', 'Second'); cy.get('input[type="checkbox"]').should('have.length', 10); }); it('is expandable when passed isExpandable', () => { - const SkeletonTableExample = ; + const SkeletonTableExample = ; cy.mount(SkeletonTableExample); cy.get('table').should('exist'); - cy.get('table thead tr').should('have.text', 'firstsecond'); + cy.get('table thead tr th').eq(0).should('have.text', 'Data expansion table header cell'); + cy.get('table thead tr th').eq(1).should('have.text', 'First'); + cy.get('table thead tr th').eq(2).should('have.text', 'Second'); cy.get('.pf-v5-c-table__toggle-icon').should('have.length', 10); }); it('can be passed a selectVariant to render radio buttons', () => { - const SkeletonTableExample = ; + const SkeletonTableExample = ; cy.mount(SkeletonTableExample); cy.get('table').should('exist'); - cy.get('table thead tr').should('have.text', 'firstsecond'); + cy.get('table thead tr th').eq(0).should('have.text', 'Data selection table header cell'); + cy.get('table thead tr th').eq(1).should('have.text', 'First'); + cy.get('table thead tr th').eq(2).should('have.text', 'Second'); cy.get('input[type="radio"]').should('have.length', 10); }); - it('can be passed custom columns', () => { + it('can be passed custom columns props', () => { const SkeletonTableExample = ( - first - , - second, - third + columns={[ + { cell: 'first', props: { sort: { columnIndex: 0, sortBy: { index: 0, direction: 'asc' } } } }, + { cell: 'second' }, + { cell: 'third' } ]} /> ); cy.mount(SkeletonTableExample); cy.get('table').should('exist'); - cy.get('table thead tr').should('have.text', 'firstsecondthird'); + cy.get('table thead tr th').eq(0).should('have.text', 'first'); + cy.get('table thead tr th').eq(1).should('have.text', 'second'); + cy.get('table thead tr th').eq(2).should('have.text', 'third'); cy.get('.pf-v5-c-table__sort-indicator').eq(0).find('path').should( 'have.attr', 'd', diff --git a/cypress/component/SkeletonTableBody.cy.tsx b/cypress/component/SkeletonTableBody.cy.tsx new file mode 100644 index 00000000..5e9e4355 --- /dev/null +++ b/cypress/component/SkeletonTableBody.cy.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { RowSelectVariant } from '@patternfly/react-table'; +import SkeletonTableBody from '@patternfly/react-component-groups/dist/dynamic/SkeletonTableBody'; + +describe('SkeletonTableBody', () => { + beforeEach(() => { + cy.viewport(1600, 800); + }); + + it('renders SkeletonTableBody', () => { + const SkeletonTableExample = ; + cy.mount(SkeletonTableExample); + cy.get('div[class="pf-v5-c-skeleton"]').should('have.length', 20); + }); + + it('contains checkboxes when passed isSelectable', () => { + cy.mount(); + cy.get('input[type="checkbox"]').should('have.length', 10); + }); + + it('is expandable when passed isExpandable', () => { + cy.mount(); + cy.get('.pf-v5-c-table__toggle-icon').should('have.length', 10); + }); + + it('can be passed a selectVariant to render radio buttons', () => { + cy.mount(); + cy.get('input[type="radio"]').should('have.length', 10); + }); +}); \ No newline at end of file diff --git a/cypress/component/SkeletonTableHead.cy.tsx b/cypress/component/SkeletonTableHead.cy.tsx new file mode 100644 index 00000000..02460dcc --- /dev/null +++ b/cypress/component/SkeletonTableHead.cy.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { Table } from '@patternfly/react-table'; +import { SkeletonTableHead } from '@patternfly/react-component-groups/dist/dynamic/SkeletonTableHead'; + +describe('SkeletonTableHead', () => { + beforeEach(() => { + cy.viewport(1600, 800); + }); + + it('renders SkeletonTable with custom columns', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').eq(0).should('have.text', 'First'); + cy.get('table thead tr th').eq(1).should('have.text', 'Second'); + }); + + it('renders SkeletonTable with column count but no custom columns', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').should('have.length', 2); + cy.get('table thead tr th').each((th) => { + cy.wrap(th).find('.pf-v5-c-skeleton').should('exist'); + }); + }); + + it('renders SkeletonTable with selectable column', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').should('have.length', 2); + cy.get('table thead tr th').eq(0).should('have.text', 'Data selection table header cell'); + }); + + it('renders SkeletonTable with expandable column', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').should('have.length', 2); + cy.get('table thead tr th').eq(0).should('have.text', 'Data expansion table header cell'); + }); + + it('renders SkeletonTable with selectable and expandable columns', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').should('have.length', 4); + }); + + it('renders SkeletonTable with custom ouiaId', () => { + const customOuiaId = 'CustomOuia'; + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('thead').should('have.attr', 'data-ouia-component-id', `${customOuiaId}-thead`); + cy.get('th').each((th, index) => { + cy.wrap(th).should('have.attr', 'data-ouia-component-id', `${customOuiaId}-th-${index}`); + }); + }); + + it('handles no columns and no columnsCount', () => { + const SkeletonTableExample = ( + + +
+ ); + cy.mount(SkeletonTableExample); + cy.get('table thead tr th').should('have.length', 1); + }); +}); diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCompactExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCompactExample.tsx deleted file mode 100644 index a05cca40..00000000 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCompactExample.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; - -export const SkeletonTableExample: React.FC = () => ; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExample.tsx deleted file mode 100644 index 2da39452..00000000 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExample.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; - -export const SkeletonTableExample: React.FC = () => \ No newline at end of file diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExpandableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExpandableExample.tsx deleted file mode 100644 index 69027eb7..00000000 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableExpandableExample.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; - -export const SkeletonTableExample: React.FC = () => ; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableLoadingExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableLoadingExample.tsx deleted file mode 100644 index c12d7b83..00000000 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableLoadingExample.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; -import { Table, Tbody, Td, Th, Tr, Thead } from '@patternfly/react-table'; -import { Button, Stack, StackItem } from '@patternfly/react-core'; - -interface Repository { - name: string; - branches: string | null; - prs: string | null; - workspaces: string; - lastCommit: string; -} - -export const SkeletonTableExample: React.FC = () => { - const [ isLoaded, setIsLoaded ] = React.useState(false); - - const simulatedAsyncCall = new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, 5000); - }); - - const loadData = async () => { - const result = await simulatedAsyncCall; - setIsLoaded(result); - }; - - 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' } - ]; - - const columnNames = { - name: 'Repositories', - branches: 'Branches', - prs: 'Pull requests', - workspaces: 'Workspaces', - lastCommit: 'Last commit' - }; - - let table: React.ReactNode; - - if (!isLoaded) { - table = ( - - ); - } else { - table = ( - - - - - - - - - - - - {repositories.map((repo) => ( - - - - - - - - ))} - -
{columnNames.name}{columnNames.branches}{columnNames.prs}{columnNames.workspaces}{columnNames.lastCommit}
{repo.name}{repo.branches}{repo.prs}{repo.workspaces}{repo.lastCommit}
- ); - } - - return ( - <> - - {table} - - - - - - ); -}; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableSelectableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableSelectableExample.tsx deleted file mode 100644 index 5249902e..00000000 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableSelectableExample.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; - -export const SkeletonTableExample: React.FC = () => ( - -); diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/Skeleton.md b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTable.md similarity index 73% rename from packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/Skeleton.md rename to packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTable.md index ddfd8333..956ad9df 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/Skeleton.md +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTable.md @@ -10,10 +10,13 @@ id: Skeleton table source: react # If you use typescript, the name of the interface to display props for # These are found through the sourceProps function provided in patternfly-docs.source.js -propComponents: ['SkeletonTable'] -sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/Skeleton.md +propComponents: ['SkeletonTable', 'SkeletonTableHead', 'SkeletonTableBody'] +sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTable.md --- +import { RowSelectVariant, TableVariant, Table } from '@patternfly/react-table'; import SkeletonTable from '@patternfly/react-component-groups/dist/dynamic/SkeletonTable'; +import SkeletonTableHead from '@patternfly/react-component-groups/dist/dynamic/SkeletonTableHead'; +import SkeletonTableBody from '@patternfly/react-component-groups/dist/dynamic/SkeletonTableBody'; The **skeleton table** component is used to display placeholder "skeletons" within a table as its contents load. @@ -63,9 +66,25 @@ Custom column headers can be provided by passing an array of strings or `Th` com The following example demonstrates the typical behavior of a skeleton table transitioning to a normal table as the data becomes available. -To simulate this loading process, select the `Reload table` button and wait for the data to populate. +To simulate this loading process, click the "Reload table" button and wait for the data to populate. ```js file="./SkeletonTableLoadingExample.tsx" ``` + +### Skeleton table head + +You can render only the `` part of the skeleton table by using the ``. + +```js file="./SkeletonTableHeadExample.tsx" + +``` + +### Skeleton table body + +You can also render only the `` part of the skeleton table by using the ``. + +```js file="./SkeletonTableBodyExample.tsx" + +``` diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableBodyExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableBodyExample.tsx new file mode 100644 index 00000000..c3f15f84 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableBodyExample.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { Table } from '@patternfly/react-table'; +import SkeletonTableBody from "@patternfly/react-component-groups/dist/dynamic/SkeletonTableBody"; + +export const SkeletonTableBodyExample: React.FC = () => ( + + +
+); \ No newline at end of file diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCompactExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCompactExample.tsx new file mode 100644 index 00000000..47316ad7 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCompactExample.tsx @@ -0,0 +1,5 @@ +import React from 'react'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable"; +import { TableVariant } from '@patternfly/react-table'; + +export const SkeletonTableExample: React.FC = () => ; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCustomExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCustomExample.tsx similarity index 59% rename from packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCustomExample.tsx rename to packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCustomExample.tsx index 2ae724c4..f44cc9df 100644 --- a/packages/module/patternfly-docs/content/extensions/component-groups/examples/Skeleton/SkeletonTableCustomExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableCustomExample.tsx @@ -1,16 +1,15 @@ import React from 'react'; -import SkeletonTable from '@patternfly/react-core/dist/js/components/Skeleton/SkeletonTable'; -import { Th } from '@patternfly/react-table'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable";import { Th } from '@patternfly/react-table'; export const SkeletonTableExample: React.FC = () => ( - first + First , - second + Second ]} /> diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExample.tsx new file mode 100644 index 00000000..1f713678 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExample.tsx @@ -0,0 +1,4 @@ +import React from 'react'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable"; + +export const SkeletonTableExample: React.FC = () => \ No newline at end of file diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExpandableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExpandableExample.tsx new file mode 100644 index 00000000..07da239c --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableExpandableExample.tsx @@ -0,0 +1,4 @@ +import React from 'react'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable"; + +export const SkeletonTableExample: React.FC = () => ; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableHeadExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableHeadExample.tsx new file mode 100644 index 00000000..6f6f6c3e --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableHeadExample.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { Table } from '@patternfly/react-table'; +import SkeletonTableHead from "@patternfly/react-component-groups/dist/dynamic/SkeletonTableHead"; + +export const SkeletonTableHeadExample: React.FC = () => ( + + +
+); \ No newline at end of file diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableLoadingExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableLoadingExample.tsx new file mode 100644 index 00000000..456466a5 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableLoadingExample.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable"; +import { Table, Tbody, Td, Th, Tr, Thead } from '@patternfly/react-table'; +import { Button, Stack, StackItem } from '@patternfly/react-core'; + +interface Repository { + name: string; + branches: string | null; + prs: string | null; + workspaces: string; + lastCommit: string; +} + +export const SkeletonTableExample: React.FC = () => { + const [ isLoaded, setIsLoaded ] = React.useState(false); + + const loadData = () => { + setIsLoaded(false); + setTimeout(() => { + setIsLoaded(true); + }, 5000); + }; + + 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' } + ]; + + const columnNames = { + name: 'Repositories', + branches: 'Branches', + prs: 'Pull requests', + workspaces: 'Workspaces', + lastCommit: 'Last commit' + }; + + const table: React.ReactNode = !isLoaded ? ( + + ) : ( + + + + + + + + + + + + {repositories.map((repo) => ( + + + + + + + + ))} + +
{columnNames.name}{columnNames.branches}{columnNames.prs}{columnNames.workspaces}{columnNames.lastCommit}
{repo.name}{repo.branches || 'N/A'}{repo.prs || 'N/A'}{repo.workspaces}{repo.lastCommit}
+ ); + + React.useEffect(() => { + loadData(); + }, []); + + return ( + <> + + {table} + + + + + + ); +}; diff --git a/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableSelectableExample.tsx b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableSelectableExample.tsx new file mode 100644 index 00000000..774a30b5 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/component-groups/examples/SkeletonTable/SkeletonTableSelectableExample.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import SkeletonTable from "@patternfly/react-component-groups/dist/dynamic/SkeletonTable"; +import { RowSelectVariant } from '@patternfly/react-table'; + +export const SkeletonTableExample: React.FC = () => ( + +); diff --git a/packages/module/src/SkeletonTable/SkeletonTable.test.tsx b/packages/module/src/SkeletonTable/SkeletonTable.test.tsx index 72a426d5..a895e311 100644 --- a/packages/module/src/SkeletonTable/SkeletonTable.test.tsx +++ b/packages/module/src/SkeletonTable/SkeletonTable.test.tsx @@ -4,10 +4,10 @@ import SkeletonTable from './SkeletonTable'; describe('SkeletonTable component', () => { it('should render correctly', () => { - expect(render()).toMatchSnapshot(); + expect(render()).toMatchSnapshot(); }); it('should render correctly with rows', () => { - expect(render()).toMatchSnapshot(); + expect(render()).toMatchSnapshot(); }); }); diff --git a/packages/module/src/SkeletonTable/SkeletonTable.tsx b/packages/module/src/SkeletonTable/SkeletonTable.tsx index 587b9009..5e0f7683 100644 --- a/packages/module/src/SkeletonTable/SkeletonTable.tsx +++ b/packages/module/src/SkeletonTable/SkeletonTable.tsx @@ -1,25 +1,22 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import { Caption, Table, TableProps, TableVariant, - Tbody, - Td, - Th, - Thead, - Tr, - RowSelectVariant + RowSelectVariant, + ThProps } from '@patternfly/react-table'; -import { Skeleton } from '@patternfly/react-core'; +import SkeletonTableBody, { SkeletonTableBodyProps } from '../SkeletonTableBody'; +import SkeletonTableHead, { SkeletonTableHeadProps } from '../SkeletonTableHead'; -export interface SkeletonTableProps extends Omit { +export interface SkeletonTableProps extends Omit, Omit, SkeletonTableHeadProps { /** Indicates the table variant */ variant?: TableVariant; /** Flag indicating if the table should have borders */ borders?: boolean; /** The number of rows the skeleton table should contain */ - rows?: number; + numberOfRows?: number; /** Any captions that should be added to the table */ caption?: React.ReactNode; /** Custom OUIA ID */ @@ -31,83 +28,46 @@ export interface SkeletonTableProps extends Omit { /** Determines if the row selection variant (radio/checkbox) */ selectVariant?: RowSelectVariant; /** Custom columns for the table */ - columns?: string[] | React.ReactElement[]; + columns?: (ReactNode | { cell: ReactNode; props?: ThProps })[]; /** Number of columns in the table */ - numberOfColumns?: number; + columnsCount?: number; } const SkeletonTable: React.FunctionComponent = ({ variant, borders = true, - rows = 5, + rowsCount = 5, caption, ouiaId = 'SkeletonTable', isSelectable, isExpandable, selectVariant = RowSelectVariant.checkbox, columns, - numberOfColumns, + columnsCount, + isTreeTable, ...rest }: SkeletonTableProps) => { - const hasCustomColumns = Array.isArray(columns); - const rowCells = hasCustomColumns ? columns?.length ?? 0 : numberOfColumns; - const rowArray = [ ...new Array(rowCells) ]; - - const bodyRows = [ ...new Array(rows) ].map((_, rowIndex) => ( - - {isExpandable && ( - - )} - {isSelectable && ( - - )} - {rowArray.map((_, colIndex) => ( - - - - ))} - - )); + const rowCellsCount = Array.isArray(columns) ? columns.length : columnsCount; return ( {caption && } - - - {isExpandable && - ) : ( - React.cloneElement(c, { key: index, 'data-ouia-component-id': `${ouiaId}-th-${index}` }) - ) - ) - : rowArray.map((_, index) => ( - - ))} - - - {bodyRows} + +
{caption}
} - {isSelectable && } - {hasCustomColumns - ? columns?.map((c, index) => - typeof c === 'string' ? ( - - {c} - - -
); }; diff --git a/packages/module/src/SkeletonTable/__snapshots__/SkeletonTable.test.tsx.snap b/packages/module/src/SkeletonTable/__snapshots__/SkeletonTable.test.tsx.snap index d3b8ddee..6995a472 100644 --- a/packages/module/src/SkeletonTable/__snapshots__/SkeletonTable.test.tsx.snap +++ b/packages/module/src/SkeletonTable/__snapshots__/SkeletonTable.test.tsx.snap @@ -15,29 +15,27 @@ exports[`SkeletonTable component should render correctly 1`] = ` > - first - - - second +
+ +
@@ -226,29 +224,27 @@ exports[`SkeletonTable component should render correctly 1`] = ` > - first - - - second +
+ +
@@ -491,32 +487,31 @@ exports[`SkeletonTable component should render correctly with rows 1`] = ` data-ouia-component-type="PF5/Table" data-ouia-safe="true" role="grid" + rows="10" > - first - - - second +
+ +
@@ -702,32 +697,31 @@ exports[`SkeletonTable component should render correctly with rows 1`] = ` data-ouia-component-type="PF5/Table" data-ouia-safe="true" role="grid" + rows="10" > - first - - - second +
+ +
diff --git a/packages/module/src/SkeletonTableBody/SkeletonTableBody.test.tsx b/packages/module/src/SkeletonTableBody/SkeletonTableBody.test.tsx new file mode 100644 index 00000000..b2d2972d --- /dev/null +++ b/packages/module/src/SkeletonTableBody/SkeletonTableBody.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import SkeletonTableBody from './SkeletonTableBody'; + +describe('SkeletonTableBody component', () => { + it('should render correctly', () => { + expect(render()).toMatchSnapshot(); + }); +}); diff --git a/packages/module/src/SkeletonTableBody/SkeletonTableBody.tsx b/packages/module/src/SkeletonTableBody/SkeletonTableBody.tsx new file mode 100644 index 00000000..fe171641 --- /dev/null +++ b/packages/module/src/SkeletonTableBody/SkeletonTableBody.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { + Tbody, + Td, + Tr, + RowSelectVariant +} from '@patternfly/react-table'; +import { Skeleton } from '@patternfly/react-core'; + +export interface SkeletonTableBodyProps { + /** Number of columns in the table */ + columnsCount: number; + /** Number of rows in the table */ + rowsCount?: number; + /** Custom OUIA ID */ + ouiaId?: string | number; + /** Flag indicating if the table is selectable */ + isSelectable?: boolean; + /** Flag indicating if the table is expandable */ + isExpandable?: boolean; + /** Determines if the row selection variant (radio/checkbox) */ + selectVariant?: RowSelectVariant; +} + +const SkeletonTableBody: React.FunctionComponent = ({ + rowsCount = 5, + ouiaId = 'SkeletonTableBody', + isSelectable, + isExpandable, + selectVariant = RowSelectVariant.checkbox, + columnsCount, +}: SkeletonTableBodyProps) => { + const bodyRows = [ ...Array(rowsCount) ].map((_, rowIndex) => ( + + {isExpandable && ( + + )} + {isSelectable && ( + + )} + {[ ...Array(columnsCount) ].map((_, colIndex) => ( + + + + ))} + + )); + + return ( + {bodyRows} + ); +}; + +export default SkeletonTableBody; diff --git a/packages/module/src/SkeletonTableBody/__snapshots__/SkeletonTableBody.test.tsx.snap b/packages/module/src/SkeletonTableBody/__snapshots__/SkeletonTableBody.test.tsx.snap new file mode 100644 index 00000000..e99cb9de --- /dev/null +++ b/packages/module/src/SkeletonTableBody/__snapshots__/SkeletonTableBody.test.tsx.snap @@ -0,0 +1,406 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SkeletonTableBody component should render correctly 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + +
+ , + "container":
+ + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/packages/module/src/SkeletonTableBody/index.ts b/packages/module/src/SkeletonTableBody/index.ts new file mode 100644 index 00000000..b38d4502 --- /dev/null +++ b/packages/module/src/SkeletonTableBody/index.ts @@ -0,0 +1,2 @@ +export { default } from './SkeletonTableBody'; +export * from './SkeletonTableBody'; diff --git a/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx b/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx new file mode 100644 index 00000000..ad137203 --- /dev/null +++ b/packages/module/src/SkeletonTableHead/SkeletonTableHead.test.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import SkeletonTableHead from './SkeletonTableHead'; + +describe('SkeletonTableHead component', () => { + it('should render correctly with count', () => { + expect(render()).toMatchSnapshot(); + }); + + it('should render correctly with count', () => { + expect(render()).toMatchSnapshot(); + }); +}); diff --git a/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx b/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx new file mode 100644 index 00000000..7a184024 --- /dev/null +++ b/packages/module/src/SkeletonTableHead/SkeletonTableHead.tsx @@ -0,0 +1,68 @@ +import React, { ReactNode, useMemo } from 'react'; +import { + Th, + ThProps, + Thead, + Tr +} from '@patternfly/react-table'; +import { Skeleton } from '@patternfly/react-core'; + +export const isThObject = (value: ReactNode | { cell: ReactNode; props?: ThProps }): value is { cell: ReactNode; props?: ThProps } => value != null && typeof value === 'object' && 'cell' in value; + +export interface SkeletonTableHeadProps { + /** Custom columns for the table */ + columns?: (ReactNode | { cell: ReactNode; props?: ThProps })[]; + /** Number of columns in the table */ + columnsCount?: number; + /** Custom OUIA ID */ + ouiaId?: string | number; + /** Flag indicating if the table is selectable */ + isSelectable?: boolean; + /** Flag indicating if the table is expandable */ + isExpandable?: boolean; + /** Flag indicating if the table is a tree table */ + isTreeTable?: boolean; +} + +export const SkeletonTableHead: React.FunctionComponent = ({ + ouiaId = 'SkeletonTableHeader', + isSelectable, + isExpandable, + columnsCount, + columns, + isTreeTable, + ...rest +}: SkeletonTableHeadProps) => { + const hasCustomColumns = Array.isArray(columns); + const rowCellsCount = hasCustomColumns ? columns.length : columnsCount; + + const cells = useMemo(() => [ + ...(isExpandable ? [ ] : []), + ...(isSelectable && !isTreeTable ? [ ] : []), + ...(hasCustomColumns ? ( + columns.map((column, index) => ( + + {isThObject(column) ? column.cell : column} + + )) + ) : ( + [ ...Array(rowCellsCount) ].map((_, index) => ( + + + + )) + )) + ] + , [ hasCustomColumns, isExpandable, isSelectable, isTreeTable, columns, rowCellsCount, ouiaId ]); + + + return ( + + + {cells} + + + ); +}; + +export default SkeletonTableHead; diff --git a/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap b/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap new file mode 100644 index 00000000..93178b10 --- /dev/null +++ b/packages/module/src/SkeletonTableHead/__snapshots__/SkeletonTableHead.test.tsx.snap @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SkeletonTableHead component should render correctly with count 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + +
+ , + "container":
+ + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`SkeletonTableHead component should render correctly with count 2`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + + + First + + + Second + + + +
+ , + "container":
+ + + + First + + + Second + + + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/packages/module/src/SkeletonTableHead/index.ts b/packages/module/src/SkeletonTableHead/index.ts new file mode 100644 index 00000000..eada2199 --- /dev/null +++ b/packages/module/src/SkeletonTableHead/index.ts @@ -0,0 +1,2 @@ +export { default } from './SkeletonTableHead'; +export * from './SkeletonTableHead'; diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts index 2f8003bb..2cb9f7c2 100644 --- a/packages/module/src/index.ts +++ b/packages/module/src/index.ts @@ -9,6 +9,12 @@ export * from './UnavailableContent'; export { default as TagCount } from './TagCount'; export * from './TagCount'; +export { default as SkeletonTableHead } from './SkeletonTableHead'; +export * from './SkeletonTableHead'; + +export { default as SkeletonTableBody } from './SkeletonTableBody'; +export * from './SkeletonTableBody'; + export { default as SkeletonTable } from './SkeletonTable'; export * from './SkeletonTable';