Skip to content

Commit 9646a11

Browse files
committed
feat(states): Improve active states and add error state
1 parent 8784729 commit 9646a11

File tree

4 files changed

+86
-56
lines changed

4 files changed

+86
-56
lines changed

packages/module/src/DataView/DataView.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@ import React from 'react';
22
import { Stack, StackItem } from '@patternfly/react-core';
33
import { DataViewSelection, InternalContextProvider } from '../InternalContext';
44

5+
export const DataViewState = {
6+
empty: 'empty',
7+
loading: 'loading',
8+
error: 'error'
9+
} as const;
10+
11+
export type DataViewState = typeof DataViewState[keyof typeof DataViewState];
12+
513
export interface DataViewProps {
614
/** Content rendered inside the data view */
715
children: React.ReactNode;
816
/** Custom OUIA ID */
917
ouiaId?: string;
1018
/** Selection context configuration */
11-
selection?: DataViewSelection
19+
selection?: DataViewSelection;
20+
/** Currently active state */
21+
activeState?: DataViewState;
1222
}
1323

1424
export type DataViewImpementationProps = Omit<DataViewProps, 'onSelect' | 'isItemSelected' | 'isItemSelectDisabled'>;
@@ -25,8 +35,8 @@ const DataViewImplementation: React.FC<DataViewImpementationProps> = ({
2535
</Stack>
2636
)
2737

28-
export const DataView: React.FC<DataViewProps> = ({ children, selection, ...props }: DataViewProps) => (
29-
<InternalContextProvider selection={selection}>
38+
export const DataView: React.FC<DataViewProps> = ({ children, selection, activeState, ...props }: DataViewProps) => (
39+
<InternalContextProvider selection={selection} activeState={activeState} >
3040
<DataViewImplementation {...props}>{children}</DataViewImplementation>
3141
</InternalContextProvider>
3242
);

packages/module/src/DataViewTableBasic/DataViewTableBasic.tsx

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import {
99
import { useInternalContext } from '../InternalContext';
1010
import { DataViewTableHeader } from '../DataViewTableHeader';
1111
import { DataViewTh, DataViewTr, isDataViewTdObject, isDataViewTrObject } from '../DataViewTable';
12+
import { DataViewState } from '../DataView/DataView';
1213

1314
export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | 'rows'> {
1415
/** Columns definition */
1516
columns: DataViewTh[];
1617
/** Current page rows */
1718
rows: DataViewTr[];
18-
/** Empty state to be displayed */
19-
emptyState?: React.ReactNode;
19+
/** States to be displayed when active */
20+
states?: Partial<Record<DataViewState, React.ReactNode>>
2021
/** Custom OUIA ID */
2122
ouiaId?: string;
2223
}
@@ -25,54 +26,56 @@ export const DataViewTableBasic: React.FC<DataViewTableBasicProps> = ({
2526
columns,
2627
rows,
2728
ouiaId = 'DataViewTableBasic',
28-
emptyState = null,
29+
states = {},
2930
...props
3031
}: DataViewTableBasicProps) => {
31-
const { selection } = useInternalContext();
32+
const { selection, activeState } = useInternalContext();
3233
const { onSelect, isSelected, isSelectDisabled } = selection ?? {};
33-
const isSelectable = useMemo(() => Boolean(onSelect && isSelected), [ onSelect, isSelected ])
34+
const isSelectable = useMemo(() => Boolean(onSelect && isSelected), [ onSelect, isSelected ]);
3435

3536
return (
3637
<Table aria-label="Data table" ouiaId={ouiaId} {...props}>
3738
<DataViewTableHeader columns={columns} ouiaId={ouiaId} />
3839
<Tbody>
39-
{rows?.length > 0 ? rows.map((row, rowIndex) => {
40-
const rowIsObject = isDataViewTrObject(row);
41-
return (
42-
<Tr key={rowIndex} ouiaId={`${ouiaId}-tr-${rowIndex}`} {...(rowIsObject && (row?.props ?? {}))}>
43-
{isSelectable && (
44-
<Td
45-
key={`select-${rowIndex}`}
46-
select={{
47-
rowIndex,
48-
onSelect: (_event, isSelecting) => {
49-
onSelect?.(isSelecting, rowIsObject ? row : [ row ])
50-
},
51-
isSelected: isSelected?.(row) || false,
52-
isDisabled: isSelectDisabled?.(row) || false,
53-
}}
54-
/>
55-
)}
56-
{(rowIsObject ? row.row : row).map((cell, colIndex) => {
57-
const cellIsObject = isDataViewTdObject(cell);
58-
return (
59-
<Td
60-
key={colIndex}
61-
{...(cellIsObject && (cell?.props ?? {}))}
62-
data-ouia-component-id={`${ouiaId}-td-${rowIndex}-${colIndex}`}
63-
>
64-
{cellIsObject ? cell.cell : cell}
65-
</Td>
66-
)
67-
})}
68-
</Tr>
69-
)}) : (
70-
<Tr key="empty" ouiaId={`${ouiaId}-tr-empty`}>
40+
{activeState && Object.keys(states).includes(activeState) ? (
41+
<Tr key={activeState} ouiaId={`${ouiaId}-tr-${activeState}`}>
7142
<Td colSpan={columns.length + Number(isSelectable)}>
72-
{emptyState}
43+
{states[activeState]}
7344
</Td>
7445
</Tr>
75-
)}
46+
) : (
47+
rows.map((row, rowIndex) => {
48+
const rowIsObject = isDataViewTrObject(row);
49+
return (
50+
<Tr key={rowIndex} ouiaId={`${ouiaId}-tr-${rowIndex}`} {...(rowIsObject && row?.props)}>
51+
{isSelectable && (
52+
<Td
53+
key={`select-${rowIndex}`}
54+
select={{
55+
rowIndex,
56+
onSelect: (_event, isSelecting) => {
57+
onSelect?.(isSelecting, rowIsObject ? row : [ row ]);
58+
},
59+
isSelected: isSelected?.(row) || false,
60+
isDisabled: isSelectDisabled?.(row) || false,
61+
}}
62+
/>
63+
)}
64+
{(rowIsObject ? row.row : row).map((cell, colIndex) => {
65+
const cellIsObject = isDataViewTdObject(cell);
66+
return (
67+
<Td
68+
key={colIndex}
69+
{...(cellIsObject && (cell?.props ?? {}))}
70+
data-ouia-component-id={`${ouiaId}-td-${rowIndex}-${colIndex}`}
71+
>
72+
{cellIsObject ? cell.cell : cell}
73+
</Td>
74+
);
75+
})}
76+
</Tr>
77+
);
78+
}))}
7679
</Tbody>
7780
</Table>
7881
);

packages/module/src/DataViewTableTree/DataViewTableTree.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { useInternalContext } from '../InternalContext';
1212
import { DataViewTableHeader } from '../DataViewTableHeader';
1313
import { DataViewTh, DataViewTrTree, isDataViewTdObject } from '../DataViewTable';
14+
import { DataViewState } from '../DataView/DataView';
1415

1516
const getDescendants = (node: DataViewTrTree): DataViewTrTree[] => (!node.children || !node.children.length) ? [ node ] : node.children.flatMap(getDescendants);
1617

@@ -35,8 +36,8 @@ export interface DataViewTableTreeProps extends Omit<TableProps, 'onSelect' | 'r
3536
columns: DataViewTh[];
3637
/** Current page rows */
3738
rows: DataViewTrTree[];
38-
/** Empty state to be displayed */
39-
emptyState?: React.ReactNode;
39+
/** States to be displayed when active */
40+
states?: Partial<Record<DataViewState, React.ReactNode>>
4041
/** Optional icon for the leaf rows */
4142
leafIcon?: React.ReactNode;
4243
/** Optional icon for the expanded parent rows */
@@ -50,20 +51,20 @@ export interface DataViewTableTreeProps extends Omit<TableProps, 'onSelect' | 'r
5051
export const DataViewTableTree: React.FC<DataViewTableTreeProps> = ({
5152
columns,
5253
rows,
53-
emptyState = null,
54+
states = {},
5455
leafIcon = null,
5556
expandedIcon = null,
5657
collapsedIcon = null,
5758
ouiaId = 'DataViewTableTree',
5859
...props
5960
}: DataViewTableTreeProps) => {
60-
const { selection } = useInternalContext();
61+
const { selection, activeState } = useInternalContext();
6162
const { onSelect, isSelected, isSelectDisabled } = selection ?? {};
6263
const [ expandedNodeIds, setExpandedNodeIds ] = React.useState<string[]>([]);
6364
const [ expandedDetailsNodeNames, setExpandedDetailsNodeIds ] = React.useState<string[]>([]);
6465

6566
const nodes = useMemo(() => {
66-
67+
6768
const renderRows = (
6869
[ node, ...remainingNodes ]: DataViewTrTree[],
6970
level = 1,
@@ -134,19 +135,31 @@ export const DataViewTableTree: React.FC<DataViewTableTreeProps> = ({
134135
};
135136

136137
return renderRows(rows);
137-
}, [ rows, expandedNodeIds, expandedDetailsNodeNames, leafIcon, expandedIcon, collapsedIcon, isSelected, onSelect, isSelectDisabled, ouiaId ]);
138+
}, [
139+
rows,
140+
expandedNodeIds,
141+
expandedDetailsNodeNames,
142+
leafIcon,
143+
expandedIcon,
144+
collapsedIcon,
145+
isSelected,
146+
onSelect,
147+
isSelectDisabled,
148+
ouiaId
149+
]);
138150

139151
return (
140152
<Table isTreeTable aria-label="Data table" ouiaId={ouiaId} {...props}>
141153
<DataViewTableHeader isTreeTable columns={columns} ouiaId={ouiaId} />
142-
<Tbody>
143-
{nodes.length > 0 ? nodes : (
144-
<Tr key="empty" ouiaId={`${ouiaId}-tr-empty`}>
154+
<Tbody>{
155+
activeState && Object.keys(states).includes(activeState) ? (
156+
<Tr key={activeState} ouiaId={`${ouiaId}-tr-${activeState}`}>
145157
<Td colSpan={columns.length}>
146-
{emptyState}
158+
{states[activeState]}
147159
</Td>
148160
</Tr>
149-
)}
161+
) : nodes
162+
}
150163
</Tbody>
151164
</Table>
152165
);

packages/module/src/InternalContext/InternalContext.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { createContext, PropsWithChildren, useContext } from 'react';
2+
import { DataViewState } from '../DataView';
23

34
export interface DataViewSelection {
45
/** Called when the selection of items changes */
@@ -11,20 +12,23 @@ export interface DataViewSelection {
1112

1213
export interface InternalContextValue {
1314
selection?: DataViewSelection;
15+
activeState?: DataViewState;
1416
}
1517

1618
export const InternalContext = createContext<InternalContextValue>({
17-
selection: undefined
19+
selection: undefined,
20+
activeState: undefined
1821
});
1922

2023
export type InternalProviderProps = PropsWithChildren<InternalContextValue>
2124

2225
export const InternalContextProvider: React.FC<InternalProviderProps> = ({
2326
children,
24-
selection
27+
selection,
28+
activeState
2529
}) => (
2630
<InternalContext.Provider
27-
value={{ selection }}
31+
value={{ selection, activeState }}
2832
>
2933
{children}
3034
</InternalContext.Provider>

0 commit comments

Comments
 (0)