Skip to content
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

Add error state to the DataView table #27

Merged
merged 4 commits into from
Sep 27, 2024
Merged
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
26 changes: 24 additions & 2 deletions cypress/component/DataViewTableBasic.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import DataViewTableBasic from '@patternfly/react-data-view/dist/esm/DataViewTableBasic';
import DataViewTableBasic from '@patternfly/react-data-view/dist/dynamic/DataViewTableBasic';
import DataView from '@patternfly/react-data-view/dist/dynamic/DataView';

interface Repository {
name: string;
Expand Down Expand Up @@ -48,7 +49,9 @@ describe('DataViewTableBasic', () => {
const ouiaId = 'data';

cy.mount(
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} emptyState="No data found" />
<DataView activeState="empty">
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} states={{ empty: "No data found" }} />
</DataView>
);

cy.get('[data-ouia-component-id="data-th-0"]').contains('Repositories');
Expand All @@ -60,4 +63,23 @@ describe('DataViewTableBasic', () => {
cy.get('[data-ouia-component-id="data-tr-empty"]').should('be.visible');
cy.get('[data-ouia-component-id="data-tr-empty"]').contains('No data found');
});

it('renders a basic data view table with an error state', () => {
const ouiaId = 'data';

cy.mount(
<DataView activeState="error">
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} states={{ error: "Some error" }} />
</DataView>
);

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-tr-error"]').should('be.visible');
cy.get('[data-ouia-component-id="data-tr-error"]').contains('Some error');
});
});
24 changes: 23 additions & 1 deletion cypress/component/DataViewTableTree.cy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { DataViewTable, DataViewTrTree } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import DataView from '@patternfly/react-data-view/dist/dynamic/DataView';

interface Repository {
name: string;
Expand Down Expand Up @@ -130,7 +131,9 @@ describe('DataViewTableTree', () => {
const ouiaId = 'tree';

cy.mount(
<DataViewTable isTreeTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} emptyState="No data found" />
<DataView activeState="empty">
<DataViewTable isTreeTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} states={{ empty: "No data found" }} />
</DataView>
);

cy.get('[data-ouia-component-id="tree-th-0"]').contains('Repositories');
Expand All @@ -142,4 +145,23 @@ describe('DataViewTableTree', () => {
cy.get('[data-ouia-component-id="tree-tr-empty"]').should('be.visible');
cy.get('[data-ouia-component-id="tree-tr-empty"]').contains('No data found');
});

it('renders a tree data view table with an error state', () => {
const ouiaId = 'data';

cy.mount(
<DataView activeState="error">
<DataViewTable isTreeTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={[]} states={{ error:"Some error" }} />
</DataView>
);

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-tr-error"]').should('be.visible');
cy.get('[data-ouia-component-id="data-tr-error"]').contains('Some error');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/mod
---
import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter, EmptyStateHeader, EmptyStateIcon } from '@patternfly/react-core';
import { CubesIcon, FolderIcon, FolderOpenIcon, LeafIcon, ExclamationCircleIcon } from '@patternfly/react-icons';
import { BulkSelect } from '@patternfly/react-component-groups';
import { BulkSelect, ErrorState } from '@patternfly/react-component-groups';
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynamic/DataView';

## Data view toolbar

Expand Down Expand Up @@ -74,8 +74,15 @@ It is also possible to disable row selection using the `isSelectDisabled` functi
```

### Empty state example
The data view table also supports displaying a custom empty state. You can pass it using the `emptyState` property and it will be displayed in case there are no rows to be rendered.
The data view table supports displaying a custom empty state. You can pass it using the `states` property and `empty` key. It will be automatically displayed in case there are no rows to be rendered.

```js file="./DataViewTableEmptyExample.tsx"

```

### Error state example
The data view table also supports displaying an error state. You can pass it using the `states` property and `error` key. It will be displayed in case the data view recieves its `state` property set to `error`.

```js file="./DataViewTableErrorExample.tsx"

```
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { CubesIcon } from '@patternfly/react-icons';
import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter, EmptyStateHeader, EmptyStateIcon } from '@patternfly/react-core';
Expand All @@ -21,7 +22,7 @@ const columns: DataViewTh[] = [ 'Repositories', 'Branches', 'Pull requests', 'Wo

const ouiaId = 'TableExample';

const emptyState = (
const empty = (
<EmptyState>
<EmptyStateHeader titleText="No data found" headingLevel="h4" icon={<EmptyStateIcon icon={CubesIcon} />} />
<EmptyStateBody>There are no matching data to be displayed.</EmptyStateBody>
Expand All @@ -38,11 +39,13 @@ const emptyState = (
);

export const BasicExample: React.FunctionComponent = () => (
<DataViewTable
aria-label='Repositories table'
ouiaId={ouiaId}
columns={columns}
rows={rows}
emptyState={emptyState}
/>
<DataView activeState={DataViewState.empty}>
<DataViewTable
aria-label='Repositories table'
ouiaId={ouiaId}
columns={columns}
rows={rows}
states={{ empty }}
/>
</DataView>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { ErrorState } from '@patternfly/react-component-groups';

interface Repository {
id: number;
name: string;
branches: string | null;
prs: string | null;
workspaces: string;
lastCommit: string;
}

const repositories: Repository[] = [];

// you can also pass props to Tr by returning { row: DataViewTd[], props: TrProps } }
const rows: DataViewTr[] = repositories.map((repository) => Object.values(repository));

const columns: DataViewTh[] = [ 'Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit' ];

const ouiaId = 'TableErrorExample';

const error = (
<ErrorState errorTitle='Unable to load data' errorDescription='There was an error retrieving data. Check your connection and reload the page.' />
);

export const BasicExample: React.FunctionComponent = () => (
<DataView activeState={DataViewState.error}>
<DataViewTable
aria-label='Repositories table'
ouiaId={ouiaId}
columns={columns}
rows={rows}
states={{ error }}
/>
</DataView>
);
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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
sortValue: 2
propComponents: ['DataView']
propComponents: ['DataView', 'DataViewState']
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Layout/Layout.md
---
import { useMemo } from 'react';
Expand Down
18 changes: 14 additions & 4 deletions packages/module/src/DataView/DataView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@ import React from 'react';
import { Stack, StackItem } from '@patternfly/react-core';
import { DataViewSelection, InternalContextProvider } from '../InternalContext';

export const DataViewState = {
empty: 'empty',
loading: 'loading',
error: 'error'
} as const;

export type DataViewState = typeof DataViewState[keyof typeof DataViewState];

export interface DataViewProps {
/** Content rendered inside the data view */
children: React.ReactNode;
/** Custom OUIA ID */
ouiaId?: string;
/** Selection context configuration */
selection?: DataViewSelection
selection?: DataViewSelection;
/** Currently active state */
activeState?: DataViewState;
}

export type DataViewImpementationProps = Omit<DataViewProps, 'onSelect' | 'isItemSelected' | 'isItemSelectDisabled'>;

const DataViewImplementation: React.FC<DataViewImpementationProps> = ({
children, ouiaId = 'DataView', ...props
}: DataViewImpementationProps) => (
<Stack data-ouia-component-id={`${ouiaId}-stack}`} {...props}>
<Stack data-ouia-component-id={`${ouiaId}-stack`} {...props}>
{React.Children.map(children, (child, index) => (
<StackItem data-ouia-component-id={`${ouiaId}-stack-item-${index}`}>
{child}
Expand All @@ -25,8 +35,8 @@ const DataViewImplementation: React.FC<DataViewImpementationProps> = ({
</Stack>
)

export const DataView: React.FC<DataViewProps> = ({ children, selection, ...props }: DataViewProps) => (
<InternalContextProvider selection={selection}>
export const DataView: React.FC<DataViewProps> = ({ children, selection, activeState, ...props }: DataViewProps) => (
<InternalContextProvider selection={selection} activeState={activeState} >
<DataViewImplementation {...props}>{children}</DataViewImplementation>
</InternalContextProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exports[`DataView component should render correctly 1`] = `
<div>
<div
class="pf-v5-l-stack"
data-ouia-component-id="DataView-stack}"
data-ouia-component-id="DataView-stack"
>
<div
class="pf-v5-l-stack__item"
Expand Down Expand Up @@ -45,7 +45,7 @@ exports[`DataView component should render correctly 1`] = `
"container": <div>
<div
class="pf-v5-l-stack"
data-ouia-component-id="DataView-stack}"
data-ouia-component-id="DataView-stack"
>
<div
class="pf-v5-l-stack__item"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { DataViewTableBasic } from './DataViewTableBasic';
import DataView from '../DataView/DataView';

interface Repository {
name: string;
Expand Down Expand Up @@ -37,7 +38,18 @@ describe('DataViewTable component', () => {

test('should render with an empty state', () => {
const { container } = render(
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} emptyState="No data found" rows={[]} />
<DataView activeState="empty">
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} states={{ empty:"No data found" }} rows={[]} />
</DataView>
);
expect(container).toMatchSnapshot();
});

test('should render with an error state', () => {
const { container } = render(
<DataView activeState="error">
<DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} states={{ error:"Some error" }} rows={[]} />
</DataView>
);
expect(container).toMatchSnapshot();
});
Expand Down
Loading