@patternfly/react-data-view 5.3.0 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/cjs/DataView/DataView.d.ts +4 -2
  2. package/dist/cjs/DataViewTableBasic/DataViewTableBasic.d.ts +4 -2
  3. package/dist/cjs/DataViewTableBasic/DataViewTableBasic.js +24 -23
  4. package/dist/cjs/DataViewTableBasic/DataViewTableBasic.test.js +10 -5
  5. package/dist/cjs/{DataViewTableHeader/DataViewTableHeader.d.ts → DataViewTableHead/DataViewTableHead.d.ts} +3 -3
  6. package/dist/cjs/{DataViewTableHeader/DataViewTableHeader.js → DataViewTableHead/DataViewTableHead.js} +6 -6
  7. package/dist/cjs/{DataViewTableHeader/DataViewTableHeader.test.js → DataViewTableHead/DataViewTableHead.test.js} +5 -5
  8. package/dist/cjs/DataViewTableHead/index.d.ts +2 -0
  9. package/dist/cjs/{DataViewTableHeader → DataViewTableHead}/index.js +3 -3
  10. package/dist/cjs/DataViewTableTree/DataViewTableTree.d.ts +4 -2
  11. package/dist/cjs/DataViewTableTree/DataViewTableTree.js +6 -5
  12. package/dist/cjs/DataViewTableTree/DataViewTableTree.test.js +7 -2
  13. package/dist/cjs/InternalContext/InternalContext.d.ts +9 -3
  14. package/dist/cjs/InternalContext/InternalContext.js +6 -2
  15. package/dist/cjs/index.d.ts +2 -2
  16. package/dist/cjs/index.js +4 -4
  17. package/dist/dynamic/DataViewTableHead/package.json +1 -0
  18. package/dist/esm/DataView/DataView.d.ts +4 -2
  19. package/dist/esm/DataViewTableBasic/DataViewTableBasic.d.ts +4 -2
  20. package/dist/esm/DataViewTableBasic/DataViewTableBasic.js +24 -23
  21. package/dist/esm/DataViewTableBasic/DataViewTableBasic.test.js +8 -3
  22. package/dist/esm/{DataViewTableHeader/DataViewTableHeader.d.ts → DataViewTableHead/DataViewTableHead.d.ts} +3 -3
  23. package/dist/esm/{DataViewTableHeader/DataViewTableHeader.js → DataViewTableHead/DataViewTableHead.js} +4 -4
  24. package/dist/esm/{DataViewTableHeader/DataViewTableHeader.test.js → DataViewTableHead/DataViewTableHead.test.js} +5 -5
  25. package/dist/esm/DataViewTableHead/index.d.ts +2 -0
  26. package/dist/esm/DataViewTableHead/index.js +2 -0
  27. package/dist/esm/DataViewTableTree/DataViewTableTree.d.ts +4 -2
  28. package/dist/esm/DataViewTableTree/DataViewTableTree.js +7 -6
  29. package/dist/esm/DataViewTableTree/DataViewTableTree.test.js +7 -2
  30. package/dist/esm/InternalContext/InternalContext.d.ts +9 -3
  31. package/dist/esm/InternalContext/InternalContext.js +7 -3
  32. package/dist/esm/index.d.ts +2 -2
  33. package/dist/esm/index.js +2 -2
  34. package/package.json +1 -1
  35. package/patternfly-docs/content/extensions/data-view/examples/Components/Components.md +17 -8
  36. package/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableEmptyExample.tsx +21 -14
  37. package/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableErrorExample.tsx +9 -2
  38. package/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableLoadingExample.tsx +27 -0
  39. package/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md +1 -0
  40. package/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx +27 -5
  41. package/src/DataView/DataView.tsx +4 -3
  42. package/src/DataViewTable/DataViewTable.tsx +1 -1
  43. package/src/DataViewTableBasic/DataViewTableBasic.test.tsx +12 -3
  44. package/src/DataViewTableBasic/DataViewTableBasic.tsx +46 -48
  45. package/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap +71 -30
  46. package/src/{DataViewTableHeader/DataViewTableHeader.test.tsx → DataViewTableHead/DataViewTableHead.test.tsx} +5 -7
  47. package/src/{DataViewTableHeader/DataViewTableHeader.tsx → DataViewTableHead/DataViewTableHead.tsx} +6 -6
  48. package/src/{DataViewTableHeader/__snapshots__/DataViewTableHeader.test.tsx.snap → DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap} +4 -4
  49. package/src/DataViewTableHead/index.ts +2 -0
  50. package/src/DataViewTableTree/DataViewTableTree.test.tsx +11 -2
  51. package/src/DataViewTableTree/DataViewTableTree.tsx +12 -16
  52. package/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap +71 -30
  53. package/src/InternalContext/InternalContext.tsx +24 -12
  54. package/src/index.ts +2 -2
  55. package/dist/cjs/DataViewTableHeader/index.d.ts +0 -2
  56. package/dist/dynamic/DataViewTableHeader/package.json +0 -1
  57. package/dist/esm/DataViewTableHeader/index.d.ts +0 -2
  58. package/dist/esm/DataViewTableHeader/index.js +0 -2
  59. package/src/DataViewTableHeader/index.ts +0 -2
  60. /package/dist/cjs/{DataViewTableHeader/DataViewTableHeader.test.d.ts → DataViewTableHead/DataViewTableHead.test.d.ts} +0 -0
  61. /package/dist/esm/{DataViewTableHeader/DataViewTableHeader.test.d.ts → DataViewTableHead/DataViewTableHead.test.d.ts} +0 -0
@@ -7,8 +7,10 @@ export interface DataViewTableTreeProps extends Omit<TableProps, 'onSelect' | 'r
7
7
  columns: DataViewTh[];
8
8
  /** Current page rows */
9
9
  rows: DataViewTrTree[];
10
- /** States to be displayed when active */
11
- states?: Partial<Record<DataViewState, React.ReactNode>>;
10
+ /** Table head states to be displayed when active */
11
+ headStates?: Partial<Record<DataViewState | string, React.ReactNode>>;
12
+ /** Table body states to be displayed when active */
13
+ bodyStates?: Partial<Record<DataViewState | string, React.ReactNode>>;
12
14
  /** Optional icon for the leaf rows */
13
15
  leafIcon?: React.ReactNode;
14
16
  /** Optional icon for the expanded parent rows */
@@ -10,9 +10,9 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import React, { useMemo } from 'react';
13
- import { Table, Tbody, Td, Tr, TreeRowWrapper, } from '@patternfly/react-table';
13
+ import { Table, Tbody, Td, TreeRowWrapper, } from '@patternfly/react-table';
14
14
  import { useInternalContext } from '../InternalContext';
15
- import { DataViewTableHeader } from '../DataViewTableHeader';
15
+ import { DataViewTableHead } from '../DataViewTableHead';
16
16
  import { isDataViewTdObject } from '../DataViewTable';
17
17
  const getDescendants = (node) => (!node.children || !node.children.length) ? [node] : node.children.flatMap(getDescendants);
18
18
  const isNodeChecked = (node, isSelected) => {
@@ -29,11 +29,13 @@ const isNodeChecked = (node, isSelected) => {
29
29
  return allSelected;
30
30
  };
31
31
  export const DataViewTableTree = (_a) => {
32
- var { columns, rows, states = {}, leafIcon = null, expandedIcon = null, collapsedIcon = null, ouiaId = 'DataViewTableTree' } = _a, props = __rest(_a, ["columns", "rows", "states", "leafIcon", "expandedIcon", "collapsedIcon", "ouiaId"]);
32
+ var { columns, rows, headStates, bodyStates, leafIcon = null, expandedIcon = null, collapsedIcon = null, ouiaId = 'DataViewTableTree' } = _a, props = __rest(_a, ["columns", "rows", "headStates", "bodyStates", "leafIcon", "expandedIcon", "collapsedIcon", "ouiaId"]);
33
33
  const { selection, activeState } = useInternalContext();
34
34
  const { onSelect, isSelected, isSelectDisabled } = selection !== null && selection !== void 0 ? selection : {};
35
35
  const [expandedNodeIds, setExpandedNodeIds] = React.useState([]);
36
36
  const [expandedDetailsNodeNames, setExpandedDetailsNodeIds] = React.useState([]);
37
+ const activeHeadState = useMemo(() => activeState ? headStates === null || headStates === void 0 ? void 0 : headStates[activeState] : undefined, [activeState, headStates]);
38
+ const activeBodyState = useMemo(() => activeState ? bodyStates === null || bodyStates === void 0 ? void 0 : bodyStates[activeState] : undefined, [activeState, bodyStates]);
37
39
  const nodes = useMemo(() => {
38
40
  const renderRows = ([node, ...remainingNodes], level = 1, posinset = 1, rowIndex = 0, isHidden = false) => {
39
41
  var _a, _b, _c, _d;
@@ -97,8 +99,7 @@ export const DataViewTableTree = (_a) => {
97
99
  ouiaId
98
100
  ]);
99
101
  return (React.createElement(Table, Object.assign({ isTreeTable: true, "aria-label": "Data table", ouiaId: ouiaId }, props),
100
- React.createElement(DataViewTableHeader, { isTreeTable: true, columns: columns, ouiaId: ouiaId }),
101
- React.createElement(Tbody, null, activeState && Object.keys(states).includes(activeState) ? (React.createElement(Tr, { key: activeState, ouiaId: `${ouiaId}-tr-${activeState}` },
102
- React.createElement(Td, { colSpan: columns.length }, states[activeState]))) : nodes)));
102
+ activeHeadState || React.createElement(DataViewTableHead, { isTreeTable: true, columns: columns, ouiaId: ouiaId }),
103
+ activeBodyState || React.createElement(Tbody, null, nodes)));
103
104
  };
104
105
  export default DataViewTableTree;
@@ -69,12 +69,17 @@ describe('DataViewTableTree component', () => {
69
69
  });
70
70
  test('should render tree table with an empty state', () => {
71
71
  const { container } = render(React.createElement(DataView, { activeState: "empty" },
72
- React.createElement(DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, states: { empty: "No data found" }, rows: [] })));
72
+ React.createElement(DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { empty: "No data found" }, rows: [] })));
73
73
  expect(container).toMatchSnapshot();
74
74
  });
75
75
  test('should render tree table with an error state', () => {
76
76
  const { container } = render(React.createElement(DataView, { activeState: "error" },
77
- React.createElement(DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, states: { error: "Some error" }, rows: [] })));
77
+ React.createElement(DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { error: "Some error" }, rows: [] })));
78
+ expect(container).toMatchSnapshot();
79
+ });
80
+ test('should render tree table with a loading state', () => {
81
+ const { container } = render(React.createElement(DataView, { activeState: "loading" },
82
+ React.createElement(DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { loading: "Data is loading" }, rows: [] })));
78
83
  expect(container).toMatchSnapshot();
79
84
  });
80
85
  });
@@ -8,12 +8,18 @@ export interface DataViewSelection {
8
8
  /** Determines if selection is disabled for a given item */
9
9
  isSelectDisabled?: (item: any) => boolean;
10
10
  }
11
- export interface InternalContextValue {
11
+ export interface InternalContextProps {
12
+ /** Data selection props */
12
13
  selection?: DataViewSelection;
13
- activeState?: DataViewState;
14
+ /** Currently active state */
15
+ activeState?: DataViewState | string;
16
+ }
17
+ export interface InternalContextValue extends InternalContextProps {
18
+ /** Flag indicating if data view is selectable (auto-calculated) */
19
+ isSelectable: boolean;
14
20
  }
15
21
  export declare const InternalContext: React.Context<InternalContextValue>;
16
- export type InternalProviderProps = PropsWithChildren<InternalContextValue>;
22
+ export type InternalProviderProps = PropsWithChildren<InternalContextProps>;
17
23
  export declare const InternalContextProvider: React.FC<InternalProviderProps>;
18
24
  export declare const useInternalContext: () => InternalContextValue;
19
25
  export default InternalContext;
@@ -1,8 +1,12 @@
1
- import React, { createContext, useContext } from 'react';
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
2
  export const InternalContext = createContext({
3
3
  selection: undefined,
4
- activeState: undefined
4
+ activeState: undefined,
5
+ isSelectable: false,
5
6
  });
6
- export const InternalContextProvider = ({ children, selection, activeState }) => (React.createElement(InternalContext.Provider, { value: { selection, activeState } }, children));
7
+ export const InternalContextProvider = ({ children, selection, activeState }) => {
8
+ const isSelectable = useMemo(() => Boolean((selection === null || selection === void 0 ? void 0 : selection.onSelect) && (selection === null || selection === void 0 ? void 0 : selection.isSelected)), [selection === null || selection === void 0 ? void 0 : selection.onSelect, selection === null || selection === void 0 ? void 0 : selection.isSelected]);
9
+ return (React.createElement(InternalContext.Provider, { value: { selection, activeState, isSelectable } }, children));
10
+ };
7
11
  export const useInternalContext = () => useContext(InternalContext);
8
12
  export default InternalContext;
@@ -5,8 +5,8 @@ export { default as DataViewToolbar } from './DataViewToolbar';
5
5
  export * from './DataViewToolbar';
6
6
  export { default as DataViewTableTree } from './DataViewTableTree';
7
7
  export * from './DataViewTableTree';
8
- export { default as DataViewTableHeader } from './DataViewTableHeader';
9
- export * from './DataViewTableHeader';
8
+ export { default as DataViewTableHead } from './DataViewTableHead';
9
+ export * from './DataViewTableHead';
10
10
  export { default as DataViewTableBasic } from './DataViewTableBasic';
11
11
  export * from './DataViewTableBasic';
12
12
  export { default as DataViewTable } from './DataViewTable';
package/dist/esm/index.js CHANGED
@@ -6,8 +6,8 @@ export { default as DataViewToolbar } from './DataViewToolbar';
6
6
  export * from './DataViewToolbar';
7
7
  export { default as DataViewTableTree } from './DataViewTableTree';
8
8
  export * from './DataViewTableTree';
9
- export { default as DataViewTableHeader } from './DataViewTableHeader';
10
- export * from './DataViewTableHeader';
9
+ export { default as DataViewTableHead } from './DataViewTableHead';
10
+ export * from './DataViewTableHead';
11
11
  export { default as DataViewTableBasic } from './DataViewTableBasic';
12
12
  export * from './DataViewTableBasic';
13
13
  export { default as DataViewTable } from './DataViewTable';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-data-view",
3
- "version": "5.3.0",
3
+ "version": "5.4.1",
4
4
  "description": "Data view used for Red Hat projects.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -16,7 +16,7 @@ sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/mod
16
16
  ---
17
17
  import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter, EmptyStateHeader, EmptyStateIcon } from '@patternfly/react-core';
18
18
  import { CubesIcon, FolderIcon, FolderOpenIcon, LeafIcon, ExclamationCircleIcon } from '@patternfly/react-icons';
19
- import { BulkSelect, ErrorState, ResponsiveAction, ResponsiveActions } from '@patternfly/react-component-groups';
19
+ import { BulkSelect, ErrorState, ResponsiveAction, ResponsiveActions, SkeletonTableHead, SkeletonTableBody } from '@patternfly/react-component-groups';
20
20
  import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
21
21
  import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
22
22
  import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
@@ -51,7 +51,7 @@ The **data view table** component renders your columns and rows definition into
51
51
 
52
52
  ### Rows and columns customization
53
53
 
54
- This example shows possible formats of `rows` and `columns` passed to the `DataViewTable` which allow you various customizations of the table header and body.
54
+ This example shows possible formats of `rows` and `columns` passed to the `DataViewTable` which allow you various customizations of the table head and body.
55
55
 
56
56
  ```js file="./DataViewTableExample.tsx"
57
57
 
@@ -59,9 +59,9 @@ This example shows possible formats of `rows` and `columns` passed to the `DataV
59
59
 
60
60
  The `DataViewTable` component accepts the following props:
61
61
 
62
- - `columns` defining the column headers of the table. Each item in the array can be a `ReactNode` (for simple headers) or an object with the following properties:
63
- - `cell` (`ReactNode`) content to display in the column header.
64
- - optional `props` (`ThProps`) to pass to the `<Th>` component, such as `width`, `sort`, and other table header cell properties.
62
+ - `columns` defining the column heads of the table. Each item in the array can be a `ReactNode` (for simple heads) or an object with the following properties:
63
+ - `cell` (`ReactNode`) content to display in the column head.
64
+ - optional `props` (`ThProps`) to pass to the `<Th>` component, such as `width`, `sort`, and other table head cell properties.
65
65
 
66
66
  - `rows` defining the rows to be displayed in the table. Each item in the array can be either an array of `DataViewTd` (for simple rows) or an object with the following properties:
67
67
  - `row` (`DataViewTd[]`) defining the content for each cell in the row.
@@ -72,28 +72,37 @@ The `DataViewTable` component accepts the following props:
72
72
 
73
73
  - optional `props` (`TableProps`) that are passed down to the `<Table>` component, except for `onSelect`, which is managed internally.
74
74
 
75
+ It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component through `selection`.
76
+
75
77
  ### Tree table example
76
78
  This example shows the tree table variant with expandable rows, custom icons for leaf and parent nodes. Tree table is turned on by passing `isTreeTable` flag to the `DataViewTable` component. You can pass `collapsedIcon`, `expandedIcon` or `leafIcon` to be displayen rows with given status. The tree table rows have to be defined in a format of object with following keys:
77
79
  - `row` (`DataViewTd[]`) defining the content for each cell in the row.
78
80
  - `id` (`string`) for the row (used to match items in selection end expand the rows).
79
81
  - optional `children` (`DataViewTrTree[]`) defining the children rows.
80
82
 
81
- It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component.
83
+ It is also possible to disable row selection using the `isSelectDisabled` function passed to the wrapping data view component through `selection`.
82
84
 
83
85
  ```js file="./DataViewTableTreeExample.tsx"
84
86
 
85
87
  ```
86
88
 
87
89
  ### Empty state example
88
- 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.
90
+ The data view table supports displaying a custom empty state. You can pass it using the the `headStates` and `bodyStates` properties and their `empty` key. It will be automatically displayed in case there are no rows to be rendered.
89
91
 
90
92
  ```js file="./DataViewTableEmptyExample.tsx"
91
93
 
92
94
  ```
93
95
 
94
96
  ### Error state example
95
- 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`.
97
+ The data view table also supports displaying an error state. You can pass it using the the `headStates` and `bodyStates` properties and their `error` key. It will be displayed in case the data view recieves its `state` property set to `error`.
96
98
 
97
99
  ```js file="./DataViewTableErrorExample.tsx"
98
100
 
99
101
  ```
102
+
103
+ ### Loading state example
104
+ The data view table also supports displaying a custom loading state. You can pass it using the `headStates` and `bodyStates` properties and their `loading` key. Your state will be displayed in case the data view recieves its `state` property set to `loading`.
105
+
106
+ ```js file="./DataViewTableLoadingExample.tsx"
107
+
108
+ ```
@@ -3,6 +3,7 @@ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynami
3
3
  import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
4
4
  import { CubesIcon } from '@patternfly/react-icons';
5
5
  import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter, EmptyStateHeader, EmptyStateIcon } from '@patternfly/react-core';
6
+ import { Tbody, Td, Tr } from '@patternfly/react-table';
6
7
 
7
8
  interface Repository {
8
9
  id: number;
@@ -23,19 +24,25 @@ const columns: DataViewTh[] = [ 'Repositories', 'Branches', 'Pull requests', 'Wo
23
24
  const ouiaId = 'TableExample';
24
25
 
25
26
  const empty = (
26
- <EmptyState>
27
- <EmptyStateHeader titleText="No data found" headingLevel="h4" icon={<EmptyStateIcon icon={CubesIcon} />} />
28
- <EmptyStateBody>There are no matching data to be displayed.</EmptyStateBody>
29
- <EmptyStateFooter>
30
- <EmptyStateActions>
31
- <Button variant="primary">Primary action</Button>
32
- </EmptyStateActions>
33
- <EmptyStateActions>
34
- <Button variant="link">Multiple</Button>
35
- <Button variant="link">Action Buttons</Button>
36
- </EmptyStateActions>
37
- </EmptyStateFooter>
38
- </EmptyState>
27
+ <Tbody>
28
+ <Tr key="loading" ouiaId={`${ouiaId}-tr-loading`}>
29
+ <Td colSpan={columns.length}>
30
+ <EmptyState>
31
+ <EmptyStateHeader titleText="No data found" headingLevel="h4" icon={<EmptyStateIcon icon={CubesIcon} />} />
32
+ <EmptyStateBody>There are no matching data to be displayed.</EmptyStateBody>
33
+ <EmptyStateFooter>
34
+ <EmptyStateActions>
35
+ <Button variant="primary">Primary action</Button>
36
+ </EmptyStateActions>
37
+ <EmptyStateActions>
38
+ <Button variant="link">Multiple</Button>
39
+ <Button variant="link">Action Buttons</Button>
40
+ </EmptyStateActions>
41
+ </EmptyStateFooter>
42
+ </EmptyState>
43
+ </Td>
44
+ </Tr>
45
+ </Tbody>
39
46
  );
40
47
 
41
48
  export const BasicExample: React.FunctionComponent = () => (
@@ -45,7 +52,7 @@ export const BasicExample: React.FunctionComponent = () => (
45
52
  ouiaId={ouiaId}
46
53
  columns={columns}
47
54
  rows={rows}
48
- states={{ empty }}
55
+ bodyStates={{ empty }}
49
56
  />
50
57
  </DataView>
51
58
  );
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynamic/DataView';
3
3
  import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
4
4
  import { ErrorState } from '@patternfly/react-component-groups';
5
+ import { Tbody, Td, Tr } from '@patternfly/react-table';
5
6
 
6
7
  interface Repository {
7
8
  id: number;
@@ -22,7 +23,13 @@ const columns: DataViewTh[] = [ 'Repositories', 'Branches', 'Pull requests', 'Wo
22
23
  const ouiaId = 'TableErrorExample';
23
24
 
24
25
  const error = (
25
- <ErrorState errorTitle='Unable to load data' errorDescription='There was an error retrieving data. Check your connection and reload the page.' />
26
+ <Tbody>
27
+ <Tr key="loading" ouiaId={`${ouiaId}-tr-loading`}>
28
+ <Td colSpan={columns.length}>
29
+ <ErrorState errorTitle='Unable to load data' errorDescription='There was an error retrieving data. Check your connection and reload the page.' />
30
+ </Td>
31
+ </Tr>
32
+ </Tbody>
26
33
  );
27
34
 
28
35
  export const BasicExample: React.FunctionComponent = () => (
@@ -32,7 +39,7 @@ export const BasicExample: React.FunctionComponent = () => (
32
39
  ouiaId={ouiaId}
33
40
  columns={columns}
34
41
  rows={rows}
35
- states={{ error }}
42
+ bodyStates={{ error }}
36
43
  />
37
44
  </DataView>
38
45
  );
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynamic/DataView';
3
+ import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
4
+ import { SkeletonTableBody, SkeletonTableHead } from '@patternfly/react-component-groups';
5
+
6
+ // you can also pass props to Tr by returning { row: DataViewTd[], props: TrProps } }
7
+ const rows: DataViewTr[] = [];
8
+
9
+ const columns: DataViewTh[] = [ 'Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit' ];
10
+
11
+ const ouiaId = 'TableExample';
12
+
13
+ const headLoading = <SkeletonTableHead columns={columns} />
14
+ const bodyLoading = <SkeletonTableBody rowsCount={5} columnsCount={columns.length} />;
15
+
16
+ export const BasicExample: React.FunctionComponent = () => (
17
+ <DataView activeState={DataViewState.loading}>
18
+ <DataViewTable
19
+ aria-label='Repositories table'
20
+ ouiaId={ouiaId}
21
+ columns={columns}
22
+ rows={rows}
23
+ headStates={{ loading: headLoading }}
24
+ bodyStates={{ loading: bodyLoading }}
25
+ />
26
+ </DataView>
27
+ );
@@ -18,6 +18,7 @@ import { Table, Tbody, Th, Thead, Tr, Td } from '@patternfly/react-table';
18
18
  import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
19
19
  import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
20
20
  import { useDataViewEventsContext, DataViewEventsContext, DataViewEventsProvider, EventTypes } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext';
21
+ import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
21
22
  import { Drawer, DrawerContent, DrawerContentBody } from '@patternfly/react-core';
22
23
 
23
24
  The **data view events context** provides a way of listening to the data view events from the outside of the component.
@@ -3,6 +3,8 @@ import { Drawer, DrawerActions, DrawerCloseButton, DrawerContent, DrawerContentB
3
3
  import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
4
4
  import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
5
5
  import { DataViewEventsProvider, EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext';
6
+ import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks';
7
+ import { ActionsColumn } from '@patternfly/react-table';
6
8
 
7
9
  interface Repository {
8
10
  name: string;
@@ -64,25 +66,45 @@ interface RepositoriesTableProps {
64
66
  selectedRepo?: Repository;
65
67
  }
66
68
 
69
+ const rowActions = [
70
+ {
71
+ title: 'Some action',
72
+ onClick: () => console.log('clicked on Some action') // eslint-disable-line no-console
73
+ },
74
+ {
75
+ title: <div>Another action</div>,
76
+ onClick: () => console.log('clicked on Another action') // eslint-disable-line no-console
77
+ },
78
+ {
79
+ isSeparator: true
80
+ },
81
+ {
82
+ title: 'Third action',
83
+ onClick: () => console.log('clicked on Third action') // eslint-disable-line no-console
84
+ }
85
+ ];
86
+
67
87
  const RepositoriesTable: React.FunctionComponent<RepositoriesTableProps> = ({ selectedRepo = undefined }) => {
88
+ const selection = useDataViewSelection({ matchOption: (a, b) => a.row[0] === b.row[0] });
68
89
  const { trigger } = useDataViewEventsContext();
69
90
  const rows = useMemo(() => {
70
- const handleRowClick = (repo: Repository | undefined) => {
71
- trigger(EventTypes.rowClick, repo);
91
+ const handleRowClick = (event, repo: Repository | undefined) => {
92
+ // prevents drawer toggle on actions or checkbox click
93
+ (event.target.matches('td') || event.target.matches('tr')) && trigger(EventTypes.rowClick, repo);
72
94
  };
73
95
 
74
96
  return repositories.map(repo => ({
75
- row: Object.values(repo),
97
+ row: [ ...Object.values(repo), { cell: <ActionsColumn items={rowActions}/>, props: { isActionCell: true } } ],
76
98
  props: {
77
99
  isClickable: true,
78
- onRowClick: () => handleRowClick(selectedRepo?.name === repo.name ? undefined : repo),
100
+ onRowClick: (event) => handleRowClick(event, selectedRepo?.name === repo.name ? undefined : repo),
79
101
  isRowSelected: selectedRepo?.name === repo.name
80
102
  }
81
103
  }));
82
104
  }, [ selectedRepo?.name, trigger ]);
83
105
 
84
106
  return (
85
- <DataView>
107
+ <DataView selection={selection}>
86
108
  <DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={rows} />
87
109
  </DataView>
88
110
  );
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Stack, StackItem } from '@patternfly/react-core';
2
+ import { Stack, StackItem, StackProps } from '@patternfly/react-core';
3
3
  import { DataViewSelection, InternalContextProvider } from '../InternalContext';
4
4
 
5
5
  export const DataViewState = {
@@ -10,7 +10,8 @@ export const DataViewState = {
10
10
 
11
11
  export type DataViewState = typeof DataViewState[keyof typeof DataViewState];
12
12
 
13
- export interface DataViewProps {
13
+ /** extends StackProps */
14
+ export interface DataViewProps extends StackProps {
14
15
  /** Content rendered inside the data view */
15
16
  children: React.ReactNode;
16
17
  /** Custom OUIA ID */
@@ -18,7 +19,7 @@ export interface DataViewProps {
18
19
  /** Selection context configuration */
19
20
  selection?: DataViewSelection;
20
21
  /** Currently active state */
21
- activeState?: DataViewState;
22
+ activeState?: DataViewState | string;
22
23
  }
23
24
 
24
25
  export type DataViewImpementationProps = Omit<DataViewProps, 'onSelect' | 'isItemSelected' | 'isItemSelectDisabled'>;
@@ -7,7 +7,7 @@ import {
7
7
  import { DataViewTableTree, DataViewTableTreeProps } from '../DataViewTableTree';
8
8
  import { DataViewTableBasic, DataViewTableBasicProps } from '../DataViewTableBasic';
9
9
 
10
- // Table header typings
10
+ // Table head typings
11
11
  export type DataViewTh = ReactNode | { cell: ReactNode; props?: ThProps };
12
12
  export const isDataViewThObject = (value: DataViewTh): value is { cell: ReactNode; props?: ThProps } => value != null && typeof value === 'object' && 'cell' in value;
13
13
 
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
+ import { DataView } from '../DataView';
3
4
  import { DataViewTableBasic } from './DataViewTableBasic';
4
- import DataView from '../DataView/DataView';
5
5
 
6
6
  interface Repository {
7
7
  name: string;
@@ -39,7 +39,7 @@ describe('DataViewTable component', () => {
39
39
  test('should render with an empty state', () => {
40
40
  const { container } = render(
41
41
  <DataView activeState="empty">
42
- <DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} states={{ empty:"No data found" }} rows={[]} />
42
+ <DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} bodyStates={{ empty: "No data found" }} rows={[]} />
43
43
  </DataView>
44
44
  );
45
45
  expect(container).toMatchSnapshot();
@@ -48,9 +48,18 @@ describe('DataViewTable component', () => {
48
48
  test('should render with an error state', () => {
49
49
  const { container } = render(
50
50
  <DataView activeState="error">
51
- <DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} states={{ error:"Some error" }} rows={[]} />
51
+ <DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} bodyStates={{ error: "Some error" }} rows={[]} />
52
52
  </DataView>
53
53
  );
54
54
  expect(container).toMatchSnapshot();
55
55
  });
56
+
57
+ test('should render with a loading state', () => {
58
+ const { container } = render(
59
+ <DataView activeState="loading">
60
+ <DataViewTableBasic aria-label='Repositories table' ouiaId={ouiaId} columns={columns} bodyStates={{ loading: "Data is loading" }} rows={[]} />
61
+ </DataView>
62
+ );
63
+ expect(container).toMatchSnapshot();
64
+ });
56
65
  });
@@ -7,7 +7,7 @@ import {
7
7
  Tr,
8
8
  } from '@patternfly/react-table';
9
9
  import { useInternalContext } from '../InternalContext';
10
- import { DataViewTableHeader } from '../DataViewTableHeader';
10
+ import { DataViewTableHead } from '../DataViewTableHead';
11
11
  import { DataViewTh, DataViewTr, isDataViewTdObject, isDataViewTrObject } from '../DataViewTable';
12
12
  import { DataViewState } from '../DataView/DataView';
13
13
 
@@ -16,8 +16,10 @@ export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | '
16
16
  columns: DataViewTh[];
17
17
  /** Current page rows */
18
18
  rows: DataViewTr[];
19
- /** States to be displayed when active */
20
- states?: Partial<Record<DataViewState, React.ReactNode>>
19
+ /** Table head states to be displayed when active */
20
+ headStates?: Partial<Record<DataViewState | string, React.ReactNode>>
21
+ /** Table body states to be displayed when active */
22
+ bodyStates?: Partial<Record<DataViewState | string, React.ReactNode>>
21
23
  /** Custom OUIA ID */
22
24
  ouiaId?: string;
23
25
  }
@@ -26,57 +28,53 @@ export const DataViewTableBasic: React.FC<DataViewTableBasicProps> = ({
26
28
  columns,
27
29
  rows,
28
30
  ouiaId = 'DataViewTableBasic',
29
- states = {},
31
+ headStates,
32
+ bodyStates,
30
33
  ...props
31
34
  }: DataViewTableBasicProps) => {
32
- const { selection, activeState } = useInternalContext();
35
+ const { selection, activeState, isSelectable } = useInternalContext();
33
36
  const { onSelect, isSelected, isSelectDisabled } = selection ?? {};
34
- const isSelectable = useMemo(() => Boolean(onSelect && isSelected), [ onSelect, isSelected ]);
37
+
38
+ const activeHeadState = useMemo(() => activeState ? headStates?.[activeState] : undefined, [ activeState, headStates ]);
39
+ const activeBodyState = useMemo(() => activeState ? bodyStates?.[activeState] : undefined, [ activeState, bodyStates ]);
40
+
41
+ const renderedRows = useMemo(() => rows.map((row, rowIndex) => {
42
+ const rowIsObject = isDataViewTrObject(row);
43
+ return (
44
+ <Tr key={rowIndex} ouiaId={`${ouiaId}-tr-${rowIndex}`} {...(rowIsObject && row?.props)}>
45
+ {isSelectable && (
46
+ <Td
47
+ key={`select-${rowIndex}`}
48
+ select={{
49
+ rowIndex,
50
+ onSelect: (_event, isSelecting) => {
51
+ onSelect?.(isSelecting, rowIsObject ? row : [ row ]);
52
+ },
53
+ isSelected: isSelected?.(row) || false,
54
+ isDisabled: isSelectDisabled?.(row) || false,
55
+ }}
56
+ />
57
+ )}
58
+ {(rowIsObject ? row.row : row).map((cell, colIndex) => {
59
+ const cellIsObject = isDataViewTdObject(cell);
60
+ return (
61
+ <Td
62
+ key={colIndex}
63
+ {...(cellIsObject && (cell?.props ?? {}))}
64
+ data-ouia-component-id={`${ouiaId}-td-${rowIndex}-${colIndex}`}
65
+ >
66
+ {cellIsObject ? cell.cell : cell}
67
+ </Td>
68
+ );
69
+ })}
70
+ </Tr>
71
+ );
72
+ }), [ rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId ]);
35
73
 
36
74
  return (
37
75
  <Table aria-label="Data table" ouiaId={ouiaId} {...props}>
38
- <DataViewTableHeader columns={columns} ouiaId={ouiaId} />
39
- <Tbody>
40
- {activeState && Object.keys(states).includes(activeState) ? (
41
- <Tr key={activeState} ouiaId={`${ouiaId}-tr-${activeState}`}>
42
- <Td colSpan={columns.length + Number(isSelectable)}>
43
- {states[activeState]}
44
- </Td>
45
- </Tr>
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
- }))}
79
- </Tbody>
76
+ { activeHeadState || <DataViewTableHead columns={columns} ouiaId={ouiaId} /> }
77
+ { activeBodyState || <Tbody>{renderedRows}</Tbody> }
80
78
  </Table>
81
79
  );
82
80
  };