@patternfly/react-data-view 6.3.0 → 6.4.0-prerelease.3

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 (55) hide show
  1. package/dist/cjs/DataViewTable/DataViewTable.d.ts +4 -0
  2. package/dist/cjs/DataViewTable/DataViewTable.js +21 -1
  3. package/dist/cjs/DataViewTableBasic/DataViewTableBasic.d.ts +2 -0
  4. package/dist/cjs/DataViewTableBasic/DataViewTableBasic.js +2 -2
  5. package/dist/cjs/DataViewTableHead/DataViewTableHead.d.ts +2 -0
  6. package/dist/cjs/DataViewTableHead/DataViewTableHead.js +5 -4
  7. package/dist/cjs/DataViewTableTree/DataViewTableTree.d.ts +2 -0
  8. package/dist/cjs/DataViewTableTree/DataViewTableTree.js +28 -1
  9. package/dist/cjs/DataViewTableTree/DataViewTableTree.test.js +4 -0
  10. package/dist/cjs/DataViewTh/DataViewTh.d.ts +32 -0
  11. package/dist/cjs/DataViewTh/DataViewTh.js +222 -0
  12. package/dist/cjs/Hooks/selection.d.ts +1 -0
  13. package/dist/cjs/Hooks/selection.js +5 -1
  14. package/dist/cjs/Hooks/selection.test.js +48 -0
  15. package/dist/cjs/InternalContext/InternalContext.d.ts +2 -0
  16. package/dist/esm/DataViewTable/DataViewTable.d.ts +4 -0
  17. package/dist/esm/DataViewTable/DataViewTable.js +21 -1
  18. package/dist/esm/DataViewTableBasic/DataViewTableBasic.d.ts +2 -0
  19. package/dist/esm/DataViewTableBasic/DataViewTableBasic.js +2 -2
  20. package/dist/esm/DataViewTableHead/DataViewTableHead.d.ts +2 -0
  21. package/dist/esm/DataViewTableHead/DataViewTableHead.js +5 -4
  22. package/dist/esm/DataViewTableTree/DataViewTableTree.d.ts +2 -0
  23. package/dist/esm/DataViewTableTree/DataViewTableTree.js +29 -2
  24. package/dist/esm/DataViewTableTree/DataViewTableTree.test.js +4 -0
  25. package/dist/esm/DataViewTh/DataViewTh.d.ts +32 -0
  26. package/dist/esm/DataViewTh/DataViewTh.js +215 -0
  27. package/dist/esm/Hooks/selection.d.ts +1 -0
  28. package/dist/esm/Hooks/selection.js +5 -1
  29. package/dist/esm/Hooks/selection.test.js +48 -0
  30. package/dist/esm/InternalContext/InternalContext.d.ts +2 -0
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +7 -7
  33. package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableResizableColumnsExample.tsx +155 -0
  34. package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableTreeExample.tsx +1 -0
  35. package/patternfly-docs/content/extensions/data-view/examples/Table/Table.md +52 -14
  36. package/patternfly-docs/content/extensions/data-view/examples/Toolbar/SelectionExample.tsx +14 -3
  37. package/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md +1 -0
  38. package/release.config.js +1 -1
  39. package/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap +0 -2
  40. package/src/DataViewFilters/__snapshots__/DataViewFilters.test.tsx.snap +0 -2
  41. package/src/DataViewTable/DataViewTable.tsx +48 -27
  42. package/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap +10 -18
  43. package/src/DataViewTableBasic/DataViewTableBasic.tsx +4 -1
  44. package/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap +20 -20
  45. package/src/DataViewTableHead/DataViewTableHead.tsx +24 -23
  46. package/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap +15 -15
  47. package/src/DataViewTableTree/DataViewTableTree.test.tsx +9 -0
  48. package/src/DataViewTableTree/DataViewTableTree.tsx +35 -1
  49. package/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap +977 -28
  50. package/src/DataViewTextFilter/__snapshots__/DataViewTextFilter.test.tsx.snap +0 -3
  51. package/src/DataViewTh/DataViewTh.tsx +342 -0
  52. package/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap +0 -10
  53. package/src/Hooks/selection.test.tsx +65 -1
  54. package/src/Hooks/selection.ts +6 -1
  55. package/src/InternalContext/InternalContext.tsx +2 -0
@@ -2,9 +2,12 @@ import { FC, ReactNode } from 'react';
2
2
  import { TdProps, ThProps, TrProps } from '@patternfly/react-table';
3
3
  import { DataViewTableTreeProps } from '../DataViewTableTree';
4
4
  import { DataViewTableBasicProps } from '../DataViewTableBasic';
5
+ import { DataViewThResizableProps } from '../DataViewTh/DataViewTh';
5
6
  export type DataViewTh = ReactNode | {
6
7
  /** Table head cell node */
7
8
  cell: ReactNode;
9
+ /** Additional props for a resizable column */
10
+ resizableProps?: DataViewThResizableProps;
8
11
  /** Props passed to Th */
9
12
  props?: ThProps;
10
13
  };
@@ -44,6 +47,7 @@ export type DataViewTableProps = ({
44
47
  isTreeTable: true;
45
48
  } & DataViewTableTreeProps) | ({
46
49
  isTreeTable?: false;
50
+ isResizable?: boolean;
47
51
  } & DataViewTableBasicProps);
48
52
  export declare const DataViewTable: FC<DataViewTableProps>;
49
53
  export default DataViewTable;
@@ -1,7 +1,19 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.DataViewTable = exports.isDataViewTrObject = exports.isDataViewTdObject = exports.isDataViewThObject = void 0;
4
15
  const jsx_runtime_1 = require("react/jsx-runtime");
16
+ const react_table_1 = require("@patternfly/react-table");
5
17
  const DataViewTableTree_1 = require("../DataViewTableTree");
6
18
  const DataViewTableBasic_1 = require("../DataViewTableBasic");
7
19
  const isDataViewThObject = (value) => value != null && typeof value === 'object' && 'cell' in value;
@@ -10,6 +22,14 @@ const isDataViewTdObject = (value) => value != null && typeof value === 'object'
10
22
  exports.isDataViewTdObject = isDataViewTdObject;
11
23
  const isDataViewTrObject = (value) => value != null && typeof value === 'object' && 'row' in value;
12
24
  exports.isDataViewTrObject = isDataViewTrObject;
13
- const DataViewTable = (props) => (props.isTreeTable ? (0, jsx_runtime_1.jsx)(DataViewTableTree_1.DataViewTableTree, Object.assign({}, props)) : (0, jsx_runtime_1.jsx)(DataViewTableBasic_1.DataViewTableBasic, Object.assign({}, props)));
25
+ const DataViewTable = (props) => {
26
+ if (props.isTreeTable) {
27
+ return (0, jsx_runtime_1.jsx)(DataViewTableTree_1.DataViewTableTree, Object.assign({}, props));
28
+ }
29
+ else {
30
+ const { isResizable } = props, rest = __rest(props, ["isResizable"]);
31
+ return isResizable ? ((0, jsx_runtime_1.jsx)(react_table_1.InnerScrollContainer, { children: (0, jsx_runtime_1.jsx)(DataViewTableBasic_1.DataViewTableBasic, Object.assign({ hasResizableColumns: true }, rest)) })) : ((0, jsx_runtime_1.jsx)(DataViewTableBasic_1.DataViewTableBasic, Object.assign({}, rest)));
32
+ }
33
+ };
14
34
  exports.DataViewTable = DataViewTable;
15
35
  exports.default = exports.DataViewTable;
@@ -14,6 +14,8 @@ export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | '
14
14
  bodyStates?: Partial<Record<DataViewState | string, React.ReactNode>>;
15
15
  /** Custom OUIA ID */
16
16
  ouiaId?: string;
17
+ /** @hide Indicates if the table is resizable */
18
+ hasResizableColumns?: boolean;
17
19
  }
18
20
  export declare const DataViewTableBasic: FC<DataViewTableBasicProps>;
19
21
  export default DataViewTableBasic;
@@ -19,7 +19,7 @@ const InternalContext_1 = require("../InternalContext");
19
19
  const DataViewTableHead_1 = require("../DataViewTableHead");
20
20
  const DataViewTable_1 = require("../DataViewTable");
21
21
  const DataViewTableBasic = (_a) => {
22
- var { columns, rows, ouiaId = 'DataViewTableBasic', headStates, bodyStates } = _a, props = __rest(_a, ["columns", "rows", "ouiaId", "headStates", "bodyStates"]);
22
+ var { columns, rows, ouiaId = 'DataViewTableBasic', headStates, bodyStates, hasResizableColumns } = _a, props = __rest(_a, ["columns", "rows", "ouiaId", "headStates", "bodyStates", "hasResizableColumns"]);
23
23
  const { selection, activeState, isSelectable } = (0, InternalContext_1.useInternalContext)();
24
24
  const { onSelect, isSelected, isSelectDisabled } = selection !== null && selection !== void 0 ? selection : {};
25
25
  const activeHeadState = (0, react_1.useMemo)(() => activeState ? headStates === null || headStates === void 0 ? void 0 : headStates[activeState] : undefined, [activeState, headStates]);
@@ -39,7 +39,7 @@ const DataViewTableBasic = (_a) => {
39
39
  return ((0, jsx_runtime_1.jsx)(react_table_1.Td, Object.assign({}, (cellIsObject && ((_a = cell === null || cell === void 0 ? void 0 : cell.props) !== null && _a !== void 0 ? _a : {})), { "data-ouia-component-id": `${ouiaId}-td-${rowIndex}-${colIndex}`, children: cellIsObject ? cell.cell : cell }), colIndex));
40
40
  })] }), rowIndex));
41
41
  }), [rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId]);
42
- return ((0, jsx_runtime_1.jsxs)(react_table_1.Table, Object.assign({ "aria-label": "Data table", ouiaId: ouiaId }, props, { children: [activeHeadState || (0, jsx_runtime_1.jsx)(DataViewTableHead_1.DataViewTableHead, { columns: columns, ouiaId: ouiaId }), activeBodyState || (0, jsx_runtime_1.jsx)(react_table_1.Tbody, { children: renderedRows })] })));
42
+ return ((0, jsx_runtime_1.jsxs)(react_table_1.Table, Object.assign({ "aria-label": "Data table", ouiaId: ouiaId }, props, { children: [activeHeadState || (0, jsx_runtime_1.jsx)(DataViewTableHead_1.DataViewTableHead, { columns: columns, ouiaId: ouiaId, hasResizableColumns: hasResizableColumns }), activeBodyState || (0, jsx_runtime_1.jsx)(react_table_1.Tbody, { children: renderedRows })] })));
43
43
  };
44
44
  exports.DataViewTableBasic = DataViewTableBasic;
45
45
  exports.default = exports.DataViewTableBasic;
@@ -9,6 +9,8 @@ export interface DataViewTableHeadProps extends TheadProps {
9
9
  columns: DataViewTh[];
10
10
  /** Custom OUIA ID */
11
11
  ouiaId?: string;
12
+ /** @hide Indicates whether table is resizable */
13
+ hasResizableColumns?: boolean;
12
14
  }
13
15
  export declare const DataViewTableHead: FC<DataViewTableHeadProps>;
14
16
  export default DataViewTableHead;
@@ -17,17 +17,18 @@ const react_1 = require("react");
17
17
  const react_table_1 = require("@patternfly/react-table");
18
18
  const InternalContext_1 = require("../InternalContext");
19
19
  const DataViewTable_1 = require("../DataViewTable");
20
+ const DataViewTh_1 = require("../DataViewTh/DataViewTh");
20
21
  const DataViewTableHead = (_a) => {
21
- var { isTreeTable = false, columns, ouiaId = 'DataViewTableHead' } = _a, props = __rest(_a, ["isTreeTable", "columns", "ouiaId"]);
22
+ var { isTreeTable = false, columns, ouiaId = 'DataViewTableHead', hasResizableColumns } = _a, props = __rest(_a, ["isTreeTable", "columns", "ouiaId", "hasResizableColumns"]);
22
23
  const { selection } = (0, InternalContext_1.useInternalContext)();
23
24
  const { onSelect, isSelected } = selection !== null && selection !== void 0 ? selection : {};
24
25
  const cells = (0, react_1.useMemo)(() => [
25
- onSelect && isSelected && !isTreeTable ? ((0, jsx_runtime_1.jsx)(react_table_1.Th, { screenReaderText: 'Data selection table head cell' }, "row-select")) : null,
26
+ onSelect && isSelected && !isTreeTable ? ((0, jsx_runtime_1.jsx)(react_table_1.Th, { screenReaderText: "Data selection table head cell" }, "row-select")) : null,
26
27
  ...columns.map((column, index) => {
27
28
  var _a;
28
- return ((0, jsx_runtime_1.jsx)(react_table_1.Th, Object.assign({}, ((0, DataViewTable_1.isDataViewThObject)(column) && ((_a = column === null || column === void 0 ? void 0 : column.props) !== null && _a !== void 0 ? _a : {})), { "data-ouia-component-id": `${ouiaId}-th-${index}`, children: (0, DataViewTable_1.isDataViewThObject)(column) ? column.cell : column }), index));
29
+ return ((0, jsx_runtime_1.jsx)(DataViewTh_1.DataViewTh, { content: (0, DataViewTable_1.isDataViewThObject)(column) ? column.cell : column, resizableProps: (0, DataViewTable_1.isDataViewThObject)(column) ? column.resizableProps : undefined, "data-ouia-component-id": `${ouiaId}-th-${index}`, thProps: (0, DataViewTable_1.isDataViewThObject)(column) ? ((_a = column === null || column === void 0 ? void 0 : column.props) !== null && _a !== void 0 ? _a : {}) : {}, hasResizableColumns: hasResizableColumns }, index));
29
30
  })
30
- ], [columns, ouiaId, onSelect, isSelected, isTreeTable]);
31
+ ], [columns, ouiaId, onSelect, isSelected, isTreeTable, hasResizableColumns]);
31
32
  return ((0, jsx_runtime_1.jsx)(react_table_1.Thead, Object.assign({ "data-ouia-component-id": `${ouiaId}-thead` }, props, { children: (0, jsx_runtime_1.jsx)(react_table_1.Tr, { ouiaId: `${ouiaId}-tr-head`, children: cells }) })));
32
33
  };
33
34
  exports.DataViewTableHead = DataViewTableHead;
@@ -18,6 +18,8 @@ export interface DataViewTableTreeProps extends Omit<TableProps, 'onSelect' | 'r
18
18
  expandedIcon?: React.ReactNode;
19
19
  /** Optional icon for the collapsed parent rows */
20
20
  collapsedIcon?: React.ReactNode;
21
+ /** Expand all expandable nodes on initial load */
22
+ expandAll?: boolean;
21
23
  /** Custom OUIA ID */
22
24
  ouiaId?: string;
23
25
  }
@@ -45,11 +45,38 @@ const getNodesAffectedBySelection = (allRows, node, isChecking, isSelected) => {
45
45
  return Array.from(affectedNodes);
46
46
  };
47
47
  const DataViewTableTree = (_a) => {
48
- 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"]);
48
+ var { columns, rows, headStates, bodyStates, leafIcon = null, expandedIcon = null, collapsedIcon = null, expandAll = false, ouiaId = 'DataViewTableTree' } = _a, props = __rest(_a, ["columns", "rows", "headStates", "bodyStates", "leafIcon", "expandedIcon", "collapsedIcon", "expandAll", "ouiaId"]);
49
49
  const { selection, activeState } = (0, InternalContext_1.useInternalContext)();
50
50
  const { onSelect, isSelected, isSelectDisabled } = selection !== null && selection !== void 0 ? selection : {};
51
51
  const [expandedNodeIds, setExpandedNodeIds] = (0, react_1.useState)([]);
52
52
  const [expandedDetailsNodeNames, setExpandedDetailsNodeIds] = (0, react_1.useState)([]);
53
+ // Helper function to collect all node IDs that have children (are expandable)
54
+ const getExpandableNodeIds = (nodes) => {
55
+ const expandableIds = [];
56
+ const traverse = (nodeList) => {
57
+ nodeList.forEach(node => {
58
+ if (node.children && node.children.length > 0) {
59
+ expandableIds.push(node.id);
60
+ traverse(node.children);
61
+ }
62
+ });
63
+ };
64
+ traverse(nodes);
65
+ return expandableIds;
66
+ };
67
+ // Effect to handle expandAll behavior
68
+ // Memoize the expandable IDs to avoid recalculating when rows object reference changes but structure is the same
69
+ const expandableIds = (0, react_1.useMemo)(() => getExpandableNodeIds(rows), [rows]);
70
+ // Effect to handle expandAll behavior - only runs when IDs actually change
71
+ (0, react_1.useEffect)(() => {
72
+ if (expandAll) {
73
+ setExpandedNodeIds(expandableIds);
74
+ }
75
+ else {
76
+ setExpandedNodeIds([]);
77
+ }
78
+ // eslint-disable-next-line react-hooks/exhaustive-deps
79
+ }, [expandAll, expandableIds.join(',')]);
53
80
  const activeHeadState = (0, react_1.useMemo)(() => activeState ? headStates === null || headStates === void 0 ? void 0 : headStates[activeState] : undefined, [activeState, headStates]);
54
81
  const activeBodyState = (0, react_1.useMemo)(() => activeState ? bodyStates === null || bodyStates === void 0 ? void 0 : bodyStates[activeState] : undefined, [activeState, bodyStates]);
55
82
  const nodes = (0, react_1.useMemo)(() => {
@@ -76,6 +76,10 @@ describe('DataViewTableTree component', () => {
76
76
  const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DataView_1.DataView, { activeState: "error", children: (0, jsx_runtime_1.jsx)(DataViewTable_1.DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { error: "Some error" }, rows: [] }) }));
77
77
  expect(container).toMatchSnapshot();
78
78
  });
79
+ test('should render tree table with all expandable nodes expanded', () => {
80
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DataView_1.DataView, { selection: mockSelection, children: (0, jsx_runtime_1.jsx)(DataViewTable_1.DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, expandAll: true, rows: rows, leafIcon: (0, jsx_runtime_1.jsx)(react_icons_1.LeafIcon, {}), expandedIcon: (0, jsx_runtime_1.jsx)(react_icons_1.FolderOpenIcon, { "aria-hidden": true }), collapsedIcon: (0, jsx_runtime_1.jsx)(react_icons_1.FolderIcon, { "aria-hidden": true }) }) }));
81
+ expect(container).toMatchSnapshot();
82
+ });
79
83
  test('should render tree table with a loading state', () => {
80
84
  const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DataView_1.DataView, { activeState: "loading", children: (0, jsx_runtime_1.jsx)(DataViewTable_1.DataViewTable, { isTreeTable: true, "aria-label": 'Repositories table', ouiaId: ouiaId, columns: columns, bodyStates: { loading: "Data is loading" }, rows: [] }) }));
81
85
  expect(container).toMatchSnapshot();
@@ -0,0 +1,32 @@
1
+ import { FC, MouseEvent as ReactMouseEvent, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react';
2
+ import { ThProps } from '@patternfly/react-table';
3
+ export interface DataViewThResizableProps {
4
+ /** Whether the column is resizable */
5
+ isResizable?: boolean;
6
+ /** Callback after the column is resized. Returns the triggering event, the column id passed in via cell props, and the new width of the column. */
7
+ onResize?: (event: ReactMouseEvent | MouseEvent | ReactKeyboardEvent | KeyboardEvent | TouchEvent, id: string | number | undefined, width: number) => void;
8
+ /** Width of the column */
9
+ width?: number;
10
+ /** Minimum width of the column */
11
+ minWidth?: number;
12
+ /** Increment for keyboard navigation */
13
+ increment?: number;
14
+ /** Increment for keyboard navigation while shift is held */
15
+ shiftIncrement?: number;
16
+ /** Provides an accessible name for the resizable column via a human readable string. */
17
+ resizeButtonAriaLabel?: string;
18
+ /** Screenreader text that gets announced when the column is resized. */
19
+ screenReaderText?: string;
20
+ }
21
+ export interface DataViewThProps {
22
+ /** Cell content */
23
+ content: ReactNode;
24
+ /** Resizable props */
25
+ resizableProps?: DataViewThResizableProps;
26
+ /** @hide Indicates whether the table has resizable columns */
27
+ hasResizableColumns?: boolean;
28
+ /** Props passed to Th */
29
+ thProps?: ThProps;
30
+ }
31
+ export declare const DataViewTh: FC<DataViewThProps>;
32
+ export default DataViewTh;
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.DataViewTh = void 0;
18
+ const jsx_runtime_1 = require("react/jsx-runtime");
19
+ const react_1 = require("react");
20
+ const react_table_1 = require("@patternfly/react-table");
21
+ const react_core_1 = require("@patternfly/react-core");
22
+ const react_jss_1 = require("react-jss");
23
+ const c_table_cell_PaddingBlockEnd_1 = __importDefault(require("@patternfly/react-tokens/dist/esm/c_table_cell_PaddingBlockEnd"));
24
+ const c_table_cell_PaddingInlineEnd_1 = __importDefault(require("@patternfly/react-tokens/dist/esm/c_table_cell_PaddingInlineEnd"));
25
+ const t_global_font_size_body_default_1 = __importDefault(require("@patternfly/react-tokens/dist/esm/t_global_font_size_body_default"));
26
+ const ResizeIcon = () => ((0, jsx_runtime_1.jsx)("svg", { className: "pf-v6-svg", viewBox: "0 0 1024 1024", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: (0, jsx_runtime_1.jsx)("path", { fillRule: "evenodd", d: "M52.7,529.8l190.5,161.9c18.6,15.9,50.5,4.7,50.5-17.8v-324c0-22.4-31.8-33.6-50.5-17.8L52.7,494.2c-11.5,9.8-11.5,25.8,0,35.6ZM480.8,12.9h62.4v998.3h-62.4V12.9ZM971.3,529.8l-190.5,161.9c-18.6,15.9-50.5,4.7-50.5-17.8v-324c0-22.4,31.8-33.6,50.5-17.8l190.5,161.9c11.5,9.8,11.5,25.8,0,35.6Z" }) }));
27
+ const useStyles = (0, react_jss_1.createUseStyles)({
28
+ dataViewResizeableTh: {
29
+ [c_table_cell_PaddingInlineEnd_1.default.name]: `calc(${t_global_font_size_body_default_1.default.var} * 2)`
30
+ },
31
+ dataViewResizableButton: {
32
+ position: 'absolute',
33
+ insetInlineEnd: `calc(${t_global_font_size_body_default_1.default.var} / 2)`,
34
+ insetBlockEnd: c_table_cell_PaddingBlockEnd_1.default.var,
35
+ cursor: 'grab',
36
+ '&:active': {
37
+ cursor: 'grabbing'
38
+ }
39
+ }
40
+ });
41
+ const DataViewTh = (_a) => {
42
+ var { content, resizableProps = {}, hasResizableColumns = false, thProps } = _a, props = __rest(_a, ["content", "resizableProps", "hasResizableColumns", "thProps"]);
43
+ const thRef = (0, react_1.useRef)(null);
44
+ const [width, setWidth] = (0, react_1.useState)((resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.width) ? resizableProps.width : 0);
45
+ // Tracks the current column width for the onResize callback, because the width state is not updated until after the resize is complete
46
+ const trackedWidth = (0, react_1.useRef)(0);
47
+ const classes = useStyles();
48
+ const isResizable = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.isResizable) || false;
49
+ const increment = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.increment) || 5;
50
+ const shiftIncrement = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.shiftIncrement) || 25;
51
+ const resizeButtonAriaLabel = resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.resizeButtonAriaLabel;
52
+ const onResize = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.onResize) || undefined;
53
+ const screenReaderText = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.screenReaderText) || `Column ${width.toFixed(0)} pixels`;
54
+ const dataViewThClassName = isResizable ? classes.dataViewResizeableTh : undefined;
55
+ const resizeButtonRef = (0, react_1.useRef)(null);
56
+ const setInitialVals = (0, react_1.useRef)(true);
57
+ const dragOffset = (0, react_1.useRef)(0);
58
+ const isResizing = (0, react_1.useRef)(false);
59
+ const isInView = (0, react_1.useRef)(true);
60
+ if (isResizable && !resizeButtonAriaLabel) {
61
+ // eslint-disable-next-line no-console
62
+ console.warn('DataViewTh: Missing resizeButtonAriaLabel. An aria label must be passed to each resizable column to provide a context specific label for its resize button.');
63
+ }
64
+ (0, react_1.useEffect)(() => {
65
+ if (!isResizable) {
66
+ return;
67
+ }
68
+ const observed = resizeButtonRef.current;
69
+ const observer = new IntersectionObserver(([entry]) => {
70
+ isInView.current = entry.isIntersecting;
71
+ }, { threshold: 0.3 });
72
+ if (observed) {
73
+ observer.observe(observed);
74
+ }
75
+ return () => {
76
+ if (observed) {
77
+ observer.unobserve(observed);
78
+ }
79
+ };
80
+ }, []);
81
+ (0, react_1.useEffect)(() => {
82
+ var _a;
83
+ if ((isResizable || hasResizableColumns) && setInitialVals.current && width === 0) {
84
+ setWidth(((_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0);
85
+ setInitialVals.current = false;
86
+ }
87
+ }, [isResizable, hasResizableColumns, setInitialVals]);
88
+ const setDragOffset = (e) => {
89
+ var _a, _b;
90
+ const isRTL = (0, react_core_1.getLanguageDirection)(thRef.current) === 'rtl';
91
+ const startingMousePos = 'clientX' in e ? e.clientX : e.touches[0].clientX;
92
+ const startingColumnPos = (isRTL ? (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left : (_b = thRef.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect().right) || 0;
93
+ dragOffset.current = startingColumnPos - startingMousePos;
94
+ };
95
+ const handleMousedown = (e) => {
96
+ e.stopPropagation();
97
+ e.preventDefault();
98
+ document.addEventListener('mousemove', callbackMouseMove);
99
+ document.addEventListener('mouseup', callbackMouseUp);
100
+ // When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
101
+ if (dragOffset.current === 0) {
102
+ setDragOffset(e);
103
+ }
104
+ isResizing.current = true;
105
+ };
106
+ const handleMouseMove = (e) => {
107
+ const mousePos = e.clientX;
108
+ handleControlMove(e, dragOffset.current + mousePos);
109
+ };
110
+ const handleTouchStart = (e) => {
111
+ e.stopPropagation();
112
+ document.addEventListener('touchmove', callbackTouchMove, { passive: false });
113
+ document.addEventListener('touchend', callbackTouchEnd);
114
+ // When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
115
+ if (dragOffset.current === 0) {
116
+ setDragOffset(e);
117
+ }
118
+ isResizing.current = true;
119
+ };
120
+ const handleTouchMove = (e) => {
121
+ e.preventDefault();
122
+ e.stopImmediatePropagation();
123
+ const touchPos = e.touches[0].clientX;
124
+ handleControlMove(e, touchPos);
125
+ };
126
+ const handleControlMove = (e, controlPosition) => {
127
+ var _a, _b;
128
+ e.stopPropagation();
129
+ if (!isResizing.current) {
130
+ return;
131
+ }
132
+ const columnRect = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
133
+ if (columnRect === undefined) {
134
+ return;
135
+ }
136
+ const mousePos = controlPosition;
137
+ const isRTL = (0, react_core_1.getLanguageDirection)(thRef.current) === 'rtl';
138
+ let newSize = isRTL ? (columnRect === null || columnRect === void 0 ? void 0 : columnRect.right) - mousePos : mousePos - (columnRect === null || columnRect === void 0 ? void 0 : columnRect.left);
139
+ // Prevent the column from shrinking below a specified minimum width
140
+ if (resizableProps.minWidth && newSize < resizableProps.minWidth) {
141
+ newSize = resizableProps.minWidth;
142
+ }
143
+ (_b = thRef.current) === null || _b === void 0 ? void 0 : _b.style.setProperty('min-width', newSize + 'px');
144
+ trackedWidth.current = newSize;
145
+ setWidth(newSize);
146
+ };
147
+ const handleMouseup = (e) => {
148
+ var _a;
149
+ if (!isResizing.current) {
150
+ return;
151
+ }
152
+ // Reset the isResizing and dragOffset values to their initial states
153
+ isResizing.current = false;
154
+ dragOffset.current = 0;
155
+ // Call the onResize callback with the new width
156
+ onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, trackedWidth.current);
157
+ // Handle scroll into view when column drag button is moved off screen
158
+ if (resizeButtonRef.current && !isInView.current) {
159
+ (_a = resizeButtonRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth', block: 'center' });
160
+ }
161
+ document.removeEventListener('mousemove', callbackMouseMove);
162
+ document.removeEventListener('mouseup', callbackMouseUp);
163
+ };
164
+ const handleTouchEnd = (e) => {
165
+ e.stopPropagation();
166
+ if (!isResizing) {
167
+ return;
168
+ }
169
+ // Reset the isResizing and dragOffset values to their initial states
170
+ isResizing.current = false;
171
+ dragOffset.current = 0;
172
+ // Call the onResize callback with the new width
173
+ onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, trackedWidth.current);
174
+ document.removeEventListener('touchmove', callbackTouchMove);
175
+ document.removeEventListener('touchend', callbackTouchEnd);
176
+ };
177
+ const callbackMouseMove = (0, react_1.useCallback)(handleMouseMove, []);
178
+ const callbackTouchEnd = (0, react_1.useCallback)(handleTouchEnd, []);
179
+ const callbackTouchMove = (0, react_1.useCallback)(handleTouchMove, []);
180
+ const callbackMouseUp = (0, react_1.useCallback)(handleMouseup, []);
181
+ const handleKeys = (e) => {
182
+ var _a, _b;
183
+ const key = e.key;
184
+ if (key === 'Tab') {
185
+ isResizing.current = false;
186
+ }
187
+ if (key !== 'ArrowLeft' && key !== 'ArrowRight') {
188
+ return;
189
+ }
190
+ e.preventDefault();
191
+ isResizing.current = true;
192
+ const isRTL = (0, react_core_1.getLanguageDirection)(thRef.current) === 'rtl';
193
+ const columnRect = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
194
+ let newSize = (columnRect === null || columnRect === void 0 ? void 0 : columnRect.width) || 0;
195
+ let delta = 0;
196
+ const _increment = e.shiftKey ? shiftIncrement : increment;
197
+ if (key === 'ArrowRight') {
198
+ if (isRTL) {
199
+ delta = -_increment;
200
+ }
201
+ else {
202
+ delta = _increment;
203
+ }
204
+ }
205
+ else if (key === 'ArrowLeft') {
206
+ if (isRTL) {
207
+ delta = _increment;
208
+ }
209
+ else {
210
+ delta = -_increment;
211
+ }
212
+ }
213
+ newSize = newSize + delta;
214
+ (_b = thRef.current) === null || _b === void 0 ? void 0 : _b.style.setProperty('min-width', newSize + 'px');
215
+ setWidth(newSize);
216
+ onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, newSize);
217
+ };
218
+ const resizableContent = ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-live": "polite", className: "pf-v6-screen-reader", children: screenReaderText }), (0, jsx_runtime_1.jsx)(react_core_1.Button, { ref: resizeButtonRef, variant: "plain", hasNoPadding: true, icon: (0, jsx_runtime_1.jsx)(ResizeIcon, {}), onMouseDown: handleMousedown, onKeyDown: handleKeys, onTouchStart: handleTouchStart, "aria-label": resizeButtonAriaLabel, className: classes.dataViewResizableButton })] }));
219
+ return ((0, jsx_runtime_1.jsx)(react_table_1.Th, Object.assign({}, thProps, props, { style: width > 0 ? { minWidth: width } : undefined, ref: thRef, modifier: "truncate", className: dataViewThClassName }, (isResizable && { additionalContent: resizableContent }), { children: content })));
220
+ };
221
+ exports.DataViewTh = DataViewTh;
222
+ exports.default = exports.DataViewTh;
@@ -8,4 +8,5 @@ export declare const useDataViewSelection: (props?: UseDataViewSelectionProps) =
8
8
  selected: any[];
9
9
  onSelect: (isSelecting: boolean, items?: any[] | any) => void;
10
10
  isSelected: (item: any) => boolean;
11
+ setSelected: (items: any[]) => void;
11
12
  };
@@ -17,10 +17,14 @@ const useDataViewSelection = (props) => {
17
17
  : setSelected(items ? prev => prev.filter(prevSelected => !(Array.isArray(items) ? items : [items]).some(item => matchOption(item, prevSelected))) : []);
18
18
  };
19
19
  const isSelected = (item) => Boolean(selected.find(selected => matchOption(selected, item)));
20
+ const setSelectedItems = (items) => {
21
+ setSelected(items);
22
+ };
20
23
  return {
21
24
  selected,
22
25
  onSelect,
23
- isSelected
26
+ isSelected,
27
+ setSelected: setSelectedItems
24
28
  };
25
29
  };
26
30
  exports.useDataViewSelection = useDataViewSelection;
@@ -19,6 +19,7 @@ describe('useDataViewSelection', () => {
19
19
  selected: [],
20
20
  onSelect: expect.any(Function),
21
21
  isSelected: expect.any(Function),
22
+ setSelected: expect.any(Function),
22
23
  });
23
24
  });
24
25
  it('should get initial state correctly - with initialSelected', () => {
@@ -28,6 +29,7 @@ describe('useDataViewSelection', () => {
28
29
  selected: initialSelected,
29
30
  onSelect: expect.any(Function),
30
31
  isSelected: expect.any(Function),
32
+ setSelected: expect.any(Function),
31
33
  });
32
34
  });
33
35
  it('should select items correctly - objects', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -52,4 +54,50 @@ describe('useDataViewSelection', () => {
52
54
  expect(result.current.isSelected({ id: 1, name: 'test1' })).toBe(true);
53
55
  expect(result.current.isSelected({ id: 3, name: 'test2' })).toBe(false);
54
56
  });
57
+ it('should have setSelected function in return object', () => {
58
+ const { result } = (0, react_1.renderHook)(() => (0, selection_1.useDataViewSelection)({ matchOption: (a, b) => a.id === b.id }));
59
+ expect(result.current).toEqual({
60
+ selected: [],
61
+ onSelect: expect.any(Function),
62
+ isSelected: expect.any(Function),
63
+ setSelected: expect.any(Function),
64
+ });
65
+ });
66
+ it('should set selected items directly using setSelected - objects', () => __awaiter(void 0, void 0, void 0, function* () {
67
+ const initialSelected = [{ id: 1, name: 'test1' }];
68
+ const { result } = (0, react_1.renderHook)(() => (0, selection_1.useDataViewSelection)({ initialSelected, matchOption: (a, b) => a.id === b.id }));
69
+ const newSelected = [{ id: 2, name: 'test2' }, { id: 3, name: 'test3' }];
70
+ yield (0, react_1.act)(() => __awaiter(void 0, void 0, void 0, function* () {
71
+ result.current.setSelected(newSelected);
72
+ }));
73
+ expect(result.current.selected).toEqual(newSelected);
74
+ }));
75
+ it('should set selected items directly using setSelected - strings', () => __awaiter(void 0, void 0, void 0, function* () {
76
+ const initialSelected = ['test1', 'test2'];
77
+ const { result } = (0, react_1.renderHook)(() => (0, selection_1.useDataViewSelection)({ initialSelected, matchOption: (a, b) => a === b }));
78
+ const newSelected = ['test3', 'test4', 'test5'];
79
+ yield (0, react_1.act)(() => __awaiter(void 0, void 0, void 0, function* () {
80
+ result.current.setSelected(newSelected);
81
+ }));
82
+ expect(result.current.selected).toEqual(newSelected);
83
+ }));
84
+ it('should clear all selections using setSelected with empty array', () => __awaiter(void 0, void 0, void 0, function* () {
85
+ const initialSelected = [{ id: 1, name: 'test1' }, { id: 2, name: 'test2' }];
86
+ const { result } = (0, react_1.renderHook)(() => (0, selection_1.useDataViewSelection)({ initialSelected, matchOption: (a, b) => a.id === b.id }));
87
+ yield (0, react_1.act)(() => __awaiter(void 0, void 0, void 0, function* () {
88
+ result.current.setSelected([]);
89
+ }));
90
+ expect(result.current.selected).toEqual([]);
91
+ }));
92
+ it('should update isSelected correctly after using setSelected', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ const initialSelected = [{ id: 1, name: 'test1' }];
94
+ const { result } = (0, react_1.renderHook)(() => (0, selection_1.useDataViewSelection)({ initialSelected, matchOption: (a, b) => a.id === b.id }));
95
+ const newSelected = [{ id: 2, name: 'test2' }, { id: 3, name: 'test3' }];
96
+ yield (0, react_1.act)(() => __awaiter(void 0, void 0, void 0, function* () {
97
+ result.current.setSelected(newSelected);
98
+ }));
99
+ expect(result.current.isSelected({ id: 1, name: 'test1' })).toBe(false);
100
+ expect(result.current.isSelected({ id: 2, name: 'test2' })).toBe(true);
101
+ expect(result.current.isSelected({ id: 3, name: 'test3' })).toBe(true);
102
+ }));
55
103
  });
@@ -5,6 +5,8 @@ export interface DataViewSelection {
5
5
  onSelect: (isSelecting: boolean, items?: any[] | any) => void;
6
6
  /** Checks if a specific item is currently selected */
7
7
  isSelected: (item: any) => boolean;
8
+ /** Directly sets the selected items */
9
+ setSelected?: (items: any[]) => void;
8
10
  /** Determines if selection is disabled for a given item */
9
11
  isSelectDisabled?: (item: any) => boolean;
10
12
  }
@@ -2,9 +2,12 @@ import { FC, ReactNode } from 'react';
2
2
  import { TdProps, ThProps, TrProps } from '@patternfly/react-table';
3
3
  import { DataViewTableTreeProps } from '../DataViewTableTree';
4
4
  import { DataViewTableBasicProps } from '../DataViewTableBasic';
5
+ import { DataViewThResizableProps } from '../DataViewTh/DataViewTh';
5
6
  export type DataViewTh = ReactNode | {
6
7
  /** Table head cell node */
7
8
  cell: ReactNode;
9
+ /** Additional props for a resizable column */
10
+ resizableProps?: DataViewThResizableProps;
8
11
  /** Props passed to Th */
9
12
  props?: ThProps;
10
13
  };
@@ -44,6 +47,7 @@ export type DataViewTableProps = ({
44
47
  isTreeTable: true;
45
48
  } & DataViewTableTreeProps) | ({
46
49
  isTreeTable?: false;
50
+ isResizable?: boolean;
47
51
  } & DataViewTableBasicProps);
48
52
  export declare const DataViewTable: FC<DataViewTableProps>;
49
53
  export default DataViewTable;
@@ -1,8 +1,28 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { InnerScrollContainer } from '@patternfly/react-table';
2
14
  import { DataViewTableTree } from '../DataViewTableTree';
3
15
  import { DataViewTableBasic } from '../DataViewTableBasic';
4
16
  export const isDataViewThObject = (value) => value != null && typeof value === 'object' && 'cell' in value;
5
17
  export const isDataViewTdObject = (value) => value != null && typeof value === 'object' && 'cell' in value;
6
18
  export const isDataViewTrObject = (value) => value != null && typeof value === 'object' && 'row' in value;
7
- export const DataViewTable = (props) => (props.isTreeTable ? _jsx(DataViewTableTree, Object.assign({}, props)) : _jsx(DataViewTableBasic, Object.assign({}, props)));
19
+ export const DataViewTable = (props) => {
20
+ if (props.isTreeTable) {
21
+ return _jsx(DataViewTableTree, Object.assign({}, props));
22
+ }
23
+ else {
24
+ const { isResizable } = props, rest = __rest(props, ["isResizable"]);
25
+ return isResizable ? (_jsx(InnerScrollContainer, { children: _jsx(DataViewTableBasic, Object.assign({ hasResizableColumns: true }, rest)) })) : (_jsx(DataViewTableBasic, Object.assign({}, rest)));
26
+ }
27
+ };
8
28
  export default DataViewTable;
@@ -14,6 +14,8 @@ export interface DataViewTableBasicProps extends Omit<TableProps, 'onSelect' | '
14
14
  bodyStates?: Partial<Record<DataViewState | string, React.ReactNode>>;
15
15
  /** Custom OUIA ID */
16
16
  ouiaId?: string;
17
+ /** @hide Indicates if the table is resizable */
18
+ hasResizableColumns?: boolean;
17
19
  }
18
20
  export declare const DataViewTableBasic: FC<DataViewTableBasicProps>;
19
21
  export default DataViewTableBasic;
@@ -16,7 +16,7 @@ import { useInternalContext } from '../InternalContext';
16
16
  import { DataViewTableHead } from '../DataViewTableHead';
17
17
  import { isDataViewTdObject, isDataViewTrObject } from '../DataViewTable';
18
18
  export const DataViewTableBasic = (_a) => {
19
- var { columns, rows, ouiaId = 'DataViewTableBasic', headStates, bodyStates } = _a, props = __rest(_a, ["columns", "rows", "ouiaId", "headStates", "bodyStates"]);
19
+ var { columns, rows, ouiaId = 'DataViewTableBasic', headStates, bodyStates, hasResizableColumns } = _a, props = __rest(_a, ["columns", "rows", "ouiaId", "headStates", "bodyStates", "hasResizableColumns"]);
20
20
  const { selection, activeState, isSelectable } = useInternalContext();
21
21
  const { onSelect, isSelected, isSelectDisabled } = selection !== null && selection !== void 0 ? selection : {};
22
22
  const activeHeadState = useMemo(() => activeState ? headStates === null || headStates === void 0 ? void 0 : headStates[activeState] : undefined, [activeState, headStates]);
@@ -36,6 +36,6 @@ export const DataViewTableBasic = (_a) => {
36
36
  return (_jsx(Td, Object.assign({}, (cellIsObject && ((_a = cell === null || cell === void 0 ? void 0 : cell.props) !== null && _a !== void 0 ? _a : {})), { "data-ouia-component-id": `${ouiaId}-td-${rowIndex}-${colIndex}`, children: cellIsObject ? cell.cell : cell }), colIndex));
37
37
  })] }), rowIndex));
38
38
  }), [rows, isSelectable, isSelected, isSelectDisabled, onSelect, ouiaId]);
39
- return (_jsxs(Table, Object.assign({ "aria-label": "Data table", ouiaId: ouiaId }, props, { children: [activeHeadState || _jsx(DataViewTableHead, { columns: columns, ouiaId: ouiaId }), activeBodyState || _jsx(Tbody, { children: renderedRows })] })));
39
+ return (_jsxs(Table, Object.assign({ "aria-label": "Data table", ouiaId: ouiaId }, props, { children: [activeHeadState || _jsx(DataViewTableHead, { columns: columns, ouiaId: ouiaId, hasResizableColumns: hasResizableColumns }), activeBodyState || _jsx(Tbody, { children: renderedRows })] })));
40
40
  };
41
41
  export default DataViewTableBasic;
@@ -9,6 +9,8 @@ export interface DataViewTableHeadProps extends TheadProps {
9
9
  columns: DataViewTh[];
10
10
  /** Custom OUIA ID */
11
11
  ouiaId?: string;
12
+ /** @hide Indicates whether table is resizable */
13
+ hasResizableColumns?: boolean;
12
14
  }
13
15
  export declare const DataViewTableHead: FC<DataViewTableHeadProps>;
14
16
  export default DataViewTableHead;