diff --git a/.eslintrc.json b/.eslintrc.json
index 47d1f488..cfd9bebc 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -52,7 +52,7 @@
"@typescript-eslint/unified-signatures": "error",
"@typescript-eslint/no-var-requires": "off",
"arrow-body-style": "error",
- "array-bracket-spacing": ["error", "always"],
+ "array-bracket-spacing": "off",
"camelcase": [
"error",
{
diff --git a/config/setupTests.js b/config/setupTests.js
index 13d962af..065d2102 100644
--- a/config/setupTests.js
+++ b/config/setupTests.js
@@ -11,8 +11,15 @@ global.MutationObserver = class {
observe(element, initObject) {}
};
+global.IntersectionObserver = class {
+ constructor(callback, options) {}
+ disconnect() {}
+ observe(element) {}
+ unobserve(element) {}
+};
+
jest.mock('react', () => ({
...jest.requireActual('react'),
- useLayoutEffect: jest.requireActual('react').useEffect,
+ useLayoutEffect: jest.requireActual('react').useEffect
}));
-Element.prototype.scrollTo = () => {};
\ No newline at end of file
+Element.prototype.scrollTo = () => {};
diff --git a/package-lock.json b/package-lock.json
index 594c0ba3..d09e7e58 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4310,10 +4310,11 @@
}
},
"node_modules/@patternfly/patternfly": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.1.0.tgz",
- "integrity": "sha512-w+QazL8NHKkg5j01eotblsswKxQQSYB0CN3yBXQL9ScpHdp/fK8M6TqWbKZNRpf+NqhMxcH/om8eR0N/fDCJqw==",
- "dev": true
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.1.tgz",
+ "integrity": "sha512-O/lTo5EHKzer/HNzqMQOQEAMG7izDDkEHpAeJ5+sGaeQ/maB3RK7sQsOPS4DjrnMxt4/cC6LogK2mowlbf1j5Q==",
+ "dev": true,
+ "peer": true
},
"node_modules/@patternfly/patternfly-a11y": {
"version": "5.1.0",
@@ -4360,54 +4361,55 @@
}
},
"node_modules/@patternfly/react-code-editor": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.1.0.tgz",
- "integrity": "sha512-ae04+DdkgXFn3wEzvNCncNa78ZK3Swh5ng8p7yqFrD6lhW69NoJf+DdQlHi8gM8Qy05DNnIemSbQWpWLpInyzw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.3.1.tgz",
+ "integrity": "sha512-lzrION96CR2G3ASjE++dX/dExH08HVcCLXbHdmiiTL4eHfbqXt4edDc+UX619XrbaccJBE+BxNNGKyO8bgpKRg==",
"dev": true,
+ "peer": true,
"dependencies": {
"@monaco-editor/react": "^4.6.0",
- "@patternfly/react-core": "^6.1.0",
- "@patternfly/react-icons": "^6.1.0",
- "@patternfly/react-styles": "^6.1.0",
+ "@patternfly/react-core": "^6.3.1",
+ "@patternfly/react-icons": "^6.3.1",
+ "@patternfly/react-styles": "^6.3.1",
"react-dropzone": "14.3.5",
"tslib": "^2.8.1"
},
"peerDependencies": {
- "react": "^17 || ^18",
- "react-dom": "^17 || ^18"
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
}
},
"node_modules/@patternfly/react-component-groups": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.1.0.tgz",
- "integrity": "sha512-8RkQv9wQk+D+nUMFtl4uk0ddMvxZ/+jFwnnXe2fw/BulouDVbpKRI24C1S8i1OQHr3++CbocBmmWRV0iw9Kvlw==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.3.0.tgz",
+ "integrity": "sha512-W8vSYD4KrAhDnjRLCPK+irVhG9GORQ7PveBFJ9FAvjCc4lGv73smDY4M1Lv2peNHQaXQpn6DSPsuynaReRvIhg==",
"dependencies": {
"@patternfly/react-core": "^6.0.0",
"@patternfly/react-icons": "^6.0.0",
+ "@patternfly/react-styles": "^6.0.0",
"@patternfly/react-table": "^6.0.0",
- "clsx": "^2.1.1",
"react-jss": "^10.10.0"
},
"peerDependencies": {
- "react": "^17 || ^18",
- "react-dom": "^17 || ^18"
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
}
},
"node_modules/@patternfly/react-core": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.1.0.tgz",
- "integrity": "sha512-zj0lJPZxQanXKD8ae2kYnweT0kpp1CzpHYAkaBjTrw2k6ZMfr/UPlp0/ugCjWEokBqh79RUADLkKJJPce/yoSQ==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz",
+ "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==",
"dependencies": {
- "@patternfly/react-icons": "^6.1.0",
- "@patternfly/react-styles": "^6.1.0",
- "@patternfly/react-tokens": "^6.1.0",
- "focus-trap": "7.6.2",
+ "@patternfly/react-icons": "^6.3.1",
+ "@patternfly/react-styles": "^6.3.1",
+ "@patternfly/react-tokens": "^6.3.1",
+ "focus-trap": "7.6.4",
"react-dropzone": "^14.3.5",
"tslib": "^2.8.1"
},
"peerDependencies": {
- "react": "^17 || ^18",
- "react-dom": "^17 || ^18"
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
}
},
"node_modules/@patternfly/react-data-view": {
@@ -4415,18 +4417,18 @@
"link": true
},
"node_modules/@patternfly/react-icons": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.1.0.tgz",
- "integrity": "sha512-V1w/j19YmOgvh72IRRf1p07k+u4M5+9P+o/IxunlF0fWzLDX4Hf+utBI11A8cRfUzpQN7eLw/vZIS3BLM8Ge3Q==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.3.1.tgz",
+ "integrity": "sha512-uiMounSIww1iZLM4pq+X8c3upzwl9iowXRPjR5CA8entb70lwgAXg3PqvypnuTAcilTq1Y3k5sFTqkhz7rgKcQ==",
"peerDependencies": {
- "react": "^17 || ^18",
- "react-dom": "^17 || ^18"
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
}
},
"node_modules/@patternfly/react-styles": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.1.0.tgz",
- "integrity": "sha512-JQ3zIl5SFiSB0YWVYibcUwgZdsp6Wn8hkfZ7KhtCjHFccSDdJexPOXVV1O9f2h4PfxTlY3YntZ81ZsguBx/Q7A=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.3.1.tgz",
+ "integrity": "sha512-hyb+PlO8YITjKh2wBvjdeZhX6FyB3hlf4r6yG4rPOHk4SgneXHjNSdGwQ3szAxgGqtbENCYtOqwD/8ai72GrxQ=="
},
"node_modules/@patternfly/react-table": {
"version": "6.1.0",
@@ -4446,9 +4448,9 @@
}
},
"node_modules/@patternfly/react-tokens": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.1.0.tgz",
- "integrity": "sha512-t1UcHbOa4txczTR5UlnG4XcAAdnDSfSlCaOddw/HTqRF59pn2ks2JUu9sfnFRZ8SiAAxKRiYdX5bT7Mf4R24+w=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.3.1.tgz",
+ "integrity": "sha512-wt/xKU1tGCDXUueFb+8/Cwxlm4vUD/Xl26O8MxbSLm6NZAHOUPwytJ7gugloGSPvc/zcsXxEgKANL8UZNO6DTw=="
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
@@ -12994,10 +12996,9 @@
}
},
"node_modules/focus-trap": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.2.tgz",
- "integrity": "sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==",
- "license": "MIT",
+ "version": "7.6.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz",
+ "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==",
"dependencies": {
"tabbable": "^6.2.0"
}
@@ -28767,17 +28768,17 @@
"license": "MIT",
"dependencies": {
"@patternfly/react-component-groups": "^6.1.0",
- "@patternfly/react-core": "^6.0.0",
- "@patternfly/react-icons": "^6.0.0",
- "@patternfly/react-table": "^6.0.0",
+ "@patternfly/react-core": "6.4.0-prerelease.1",
+ "@patternfly/react-icons": "6.4.0-prerelease.1",
+ "@patternfly/react-table": "6.4.0-prerelease.2",
"clsx": "^2.1.1",
"react-jss": "^10.10.0"
},
"devDependencies": {
"@patternfly/documentation-framework": "^6.5.20",
- "@patternfly/patternfly": "^6.0.0",
+ "@patternfly/patternfly": "6.4.0-prerelease.1",
"@patternfly/patternfly-a11y": "^5.1.0",
- "@patternfly/react-code-editor": "^6.0.0",
+ "@patternfly/react-code-editor": "6.4.0-prerelease.1",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@types/react-router-dom": "^5.3.3",
@@ -28792,6 +28793,86 @@
"react": "^17 || ^18 || ^19",
"react-dom": "^17 || ^18 || ^19"
}
+ },
+ "packages/module/node_modules/@patternfly/patternfly": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-jOLzGhRJJTJsmLV7jjFNUz+VrRs/2RTM2vtupi2K3s1rGogA3jSfUqZ5VpeHLbCQp/96WSPEjFKqyYK4iLuC0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=20.18.3 <22"
+ }
+ },
+ "packages/module/node_modules/@patternfly/react-code-editor": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-x73NmyCsVC21mBeDPX5BestP5dv+0nQWZTjT774RnYjIWzGqjGryZ6qYQUdFi6WE0oembbRkAhSYLfrKuun6vA==",
+ "dev": true,
+ "dependencies": {
+ "@monaco-editor/react": "^4.6.0",
+ "@patternfly/react-core": "^6.4.0-prerelease.1",
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "react-dropzone": "14.3.5",
+ "tslib": "^2.8.1"
+ },
+ "peerDependencies": {
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ }
+ },
+ "packages/module/node_modules/@patternfly/react-core": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-3HNvK/EpmkkDoEjNi99CqX6jLgTnjOLe5ZPsNoLixQkSfzK873YCynRMewdgpP5W3CsjN2HwCAL8ZZt0ZjRCJw==",
+ "dependencies": {
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "@patternfly/react-tokens": "^6.4.0-prerelease.1",
+ "focus-trap": "7.6.4",
+ "react-dropzone": "^14.3.5",
+ "tslib": "^2.8.1"
+ },
+ "peerDependencies": {
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ }
+ },
+ "packages/module/node_modules/@patternfly/react-icons": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-hJhiLOPOcAZjwgNIA6dytoJCC/EMTLwslLwyLZNZBNu5jjK4ymf1DmZMgC4KNhIaPBrf7rQ6WTzg2AEl6zD6/A==",
+ "peerDependencies": {
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ }
+ },
+ "packages/module/node_modules/@patternfly/react-styles": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-UlS6BjZQii8xbevK5J9CXfRBKo3GZV4O0xUKERoc6uxzDHeWSAMIF9E44SleN5G+Z2FdDst9N7ZbQlxtQbxmfA=="
+ },
+ "packages/module/node_modules/@patternfly/react-table": {
+ "version": "6.4.0-prerelease.2",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.4.0-prerelease.2.tgz",
+ "integrity": "sha512-GqKKgUDfgWQAyltt7MA0ty0fS9TdU5lkUXi0wZkPntPqX4ZjQDN/aMnsQ8xoGakh3/qnms/VPYABdMGaLBGZFQ==",
+ "dependencies": {
+ "@patternfly/react-core": "^6.4.0-prerelease.1",
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "@patternfly/react-tokens": "^6.4.0-prerelease.1",
+ "lodash": "^4.17.21",
+ "tslib": "^2.8.1"
+ },
+ "peerDependencies": {
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ }
+ },
+ "packages/module/node_modules/@patternfly/react-tokens": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-507dlMylOPcRqzhbYHTgQQIhfT3KNJYTX0mTnNQM5jHBsM+UFpfPbs1O6qMmX/+XniVHNtJKNRzgSG2UOhOHtg=="
}
},
"dependencies": {
@@ -31606,10 +31687,11 @@
}
},
"@patternfly/patternfly": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.1.0.tgz",
- "integrity": "sha512-w+QazL8NHKkg5j01eotblsswKxQQSYB0CN3yBXQL9ScpHdp/fK8M6TqWbKZNRpf+NqhMxcH/om8eR0N/fDCJqw==",
- "dev": true
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.3.1.tgz",
+ "integrity": "sha512-O/lTo5EHKzer/HNzqMQOQEAMG7izDDkEHpAeJ5+sGaeQ/maB3RK7sQsOPS4DjrnMxt4/cC6LogK2mowlbf1j5Q==",
+ "dev": true,
+ "peer": true
},
"@patternfly/patternfly-a11y": {
"version": "5.1.0",
@@ -31636,40 +31718,41 @@
}
},
"@patternfly/react-code-editor": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.1.0.tgz",
- "integrity": "sha512-ae04+DdkgXFn3wEzvNCncNa78ZK3Swh5ng8p7yqFrD6lhW69NoJf+DdQlHi8gM8Qy05DNnIemSbQWpWLpInyzw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.3.1.tgz",
+ "integrity": "sha512-lzrION96CR2G3ASjE++dX/dExH08HVcCLXbHdmiiTL4eHfbqXt4edDc+UX619XrbaccJBE+BxNNGKyO8bgpKRg==",
"dev": true,
+ "peer": true,
"requires": {
"@monaco-editor/react": "^4.6.0",
- "@patternfly/react-core": "^6.1.0",
- "@patternfly/react-icons": "^6.1.0",
- "@patternfly/react-styles": "^6.1.0",
+ "@patternfly/react-core": "^6.3.1",
+ "@patternfly/react-icons": "^6.3.1",
+ "@patternfly/react-styles": "^6.3.1",
"react-dropzone": "14.3.5",
"tslib": "^2.8.1"
}
},
"@patternfly/react-component-groups": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.1.0.tgz",
- "integrity": "sha512-8RkQv9wQk+D+nUMFtl4uk0ddMvxZ/+jFwnnXe2fw/BulouDVbpKRI24C1S8i1OQHr3++CbocBmmWRV0iw9Kvlw==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-6.3.0.tgz",
+ "integrity": "sha512-W8vSYD4KrAhDnjRLCPK+irVhG9GORQ7PveBFJ9FAvjCc4lGv73smDY4M1Lv2peNHQaXQpn6DSPsuynaReRvIhg==",
"requires": {
"@patternfly/react-core": "^6.0.0",
"@patternfly/react-icons": "^6.0.0",
+ "@patternfly/react-styles": "^6.0.0",
"@patternfly/react-table": "^6.0.0",
- "clsx": "^2.1.1",
"react-jss": "^10.10.0"
}
},
"@patternfly/react-core": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.1.0.tgz",
- "integrity": "sha512-zj0lJPZxQanXKD8ae2kYnweT0kpp1CzpHYAkaBjTrw2k6ZMfr/UPlp0/ugCjWEokBqh79RUADLkKJJPce/yoSQ==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.3.1.tgz",
+ "integrity": "sha512-1qV20nU4M6PA28qnikH9fPLQlkteaZZToFlATjBNBw7aUI6zIvj7U0akkHz8raWcfHAI+tAzGV7dfKjiv035/g==",
"requires": {
- "@patternfly/react-icons": "^6.1.0",
- "@patternfly/react-styles": "^6.1.0",
- "@patternfly/react-tokens": "^6.1.0",
- "focus-trap": "7.6.2",
+ "@patternfly/react-icons": "^6.3.1",
+ "@patternfly/react-styles": "^6.3.1",
+ "@patternfly/react-tokens": "^6.3.1",
+ "focus-trap": "7.6.4",
"react-dropzone": "^14.3.5",
"tslib": "^2.8.1"
}
@@ -31678,13 +31761,13 @@
"version": "file:packages/module",
"requires": {
"@patternfly/documentation-framework": "^6.5.20",
- "@patternfly/patternfly": "^6.0.0",
+ "@patternfly/patternfly": "6.4.0-prerelease.1",
"@patternfly/patternfly-a11y": "^5.1.0",
- "@patternfly/react-code-editor": "^6.0.0",
+ "@patternfly/react-code-editor": "6.4.0-prerelease.1",
"@patternfly/react-component-groups": "^6.1.0",
- "@patternfly/react-core": "^6.0.0",
- "@patternfly/react-icons": "^6.0.0",
- "@patternfly/react-table": "^6.0.0",
+ "@patternfly/react-core": "6.4.0-prerelease.1",
+ "@patternfly/react-icons": "6.4.0-prerelease.1",
+ "@patternfly/react-table": "6.4.0-prerelease.2",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@types/react-router-dom": "^5.3.3",
@@ -31696,18 +31779,82 @@
"react-router-dom": "^6.30.1",
"rimraf": "^6.0.1",
"typescript": "^5.9.2"
+ },
+ "dependencies": {
+ "@patternfly/patternfly": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-jOLzGhRJJTJsmLV7jjFNUz+VrRs/2RTM2vtupi2K3s1rGogA3jSfUqZ5VpeHLbCQp/96WSPEjFKqyYK4iLuC0Q==",
+ "dev": true
+ },
+ "@patternfly/react-code-editor": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-x73NmyCsVC21mBeDPX5BestP5dv+0nQWZTjT774RnYjIWzGqjGryZ6qYQUdFi6WE0oembbRkAhSYLfrKuun6vA==",
+ "dev": true,
+ "requires": {
+ "@monaco-editor/react": "^4.6.0",
+ "@patternfly/react-core": "^6.4.0-prerelease.1",
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "react-dropzone": "14.3.5",
+ "tslib": "^2.8.1"
+ }
+ },
+ "@patternfly/react-core": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-3HNvK/EpmkkDoEjNi99CqX6jLgTnjOLe5ZPsNoLixQkSfzK873YCynRMewdgpP5W3CsjN2HwCAL8ZZt0ZjRCJw==",
+ "requires": {
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "@patternfly/react-tokens": "^6.4.0-prerelease.1",
+ "focus-trap": "7.6.4",
+ "react-dropzone": "^14.3.5",
+ "tslib": "^2.8.1"
+ }
+ },
+ "@patternfly/react-icons": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-hJhiLOPOcAZjwgNIA6dytoJCC/EMTLwslLwyLZNZBNu5jjK4ymf1DmZMgC4KNhIaPBrf7rQ6WTzg2AEl6zD6/A==",
+ "requires": {}
+ },
+ "@patternfly/react-styles": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-UlS6BjZQii8xbevK5J9CXfRBKo3GZV4O0xUKERoc6uxzDHeWSAMIF9E44SleN5G+Z2FdDst9N7ZbQlxtQbxmfA=="
+ },
+ "@patternfly/react-table": {
+ "version": "6.4.0-prerelease.2",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.4.0-prerelease.2.tgz",
+ "integrity": "sha512-GqKKgUDfgWQAyltt7MA0ty0fS9TdU5lkUXi0wZkPntPqX4ZjQDN/aMnsQ8xoGakh3/qnms/VPYABdMGaLBGZFQ==",
+ "requires": {
+ "@patternfly/react-core": "^6.4.0-prerelease.1",
+ "@patternfly/react-icons": "^6.4.0-prerelease.1",
+ "@patternfly/react-styles": "^6.4.0-prerelease.1",
+ "@patternfly/react-tokens": "^6.4.0-prerelease.1",
+ "lodash": "^4.17.21",
+ "tslib": "^2.8.1"
+ }
+ },
+ "@patternfly/react-tokens": {
+ "version": "6.4.0-prerelease.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.4.0-prerelease.1.tgz",
+ "integrity": "sha512-507dlMylOPcRqzhbYHTgQQIhfT3KNJYTX0mTnNQM5jHBsM+UFpfPbs1O6qMmX/+XniVHNtJKNRzgSG2UOhOHtg=="
+ }
}
},
"@patternfly/react-icons": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.1.0.tgz",
- "integrity": "sha512-V1w/j19YmOgvh72IRRf1p07k+u4M5+9P+o/IxunlF0fWzLDX4Hf+utBI11A8cRfUzpQN7eLw/vZIS3BLM8Ge3Q==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.3.1.tgz",
+ "integrity": "sha512-uiMounSIww1iZLM4pq+X8c3upzwl9iowXRPjR5CA8entb70lwgAXg3PqvypnuTAcilTq1Y3k5sFTqkhz7rgKcQ==",
"requires": {}
},
"@patternfly/react-styles": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.1.0.tgz",
- "integrity": "sha512-JQ3zIl5SFiSB0YWVYibcUwgZdsp6Wn8hkfZ7KhtCjHFccSDdJexPOXVV1O9f2h4PfxTlY3YntZ81ZsguBx/Q7A=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.3.1.tgz",
+ "integrity": "sha512-hyb+PlO8YITjKh2wBvjdeZhX6FyB3hlf4r6yG4rPOHk4SgneXHjNSdGwQ3szAxgGqtbENCYtOqwD/8ai72GrxQ=="
},
"@patternfly/react-table": {
"version": "6.1.0",
@@ -31723,9 +31870,9 @@
}
},
"@patternfly/react-tokens": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.1.0.tgz",
- "integrity": "sha512-t1UcHbOa4txczTR5UlnG4XcAAdnDSfSlCaOddw/HTqRF59pn2ks2JUu9sfnFRZ8SiAAxKRiYdX5bT7Mf4R24+w=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.3.1.tgz",
+ "integrity": "sha512-wt/xKU1tGCDXUueFb+8/Cwxlm4vUD/Xl26O8MxbSLm6NZAHOUPwytJ7gugloGSPvc/zcsXxEgKANL8UZNO6DTw=="
},
"@pkgjs/parseargs": {
"version": "0.11.0",
@@ -37730,9 +37877,9 @@
}
},
"focus-trap": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.2.tgz",
- "integrity": "sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==",
+ "version": "7.6.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz",
+ "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==",
"requires": {
"tabbable": "^6.2.0"
}
diff --git a/packages/module/package.json b/packages/module/package.json
index 475079c2..e53d7fdd 100644
--- a/packages/module/package.json
+++ b/packages/module/package.json
@@ -32,9 +32,9 @@
},
"dependencies": {
"@patternfly/react-component-groups": "^6.1.0",
- "@patternfly/react-core": "^6.0.0",
- "@patternfly/react-icons": "^6.0.0",
- "@patternfly/react-table": "^6.0.0",
+ "@patternfly/react-core": "6.4.0-prerelease.1",
+ "@patternfly/react-icons": "6.4.0-prerelease.1",
+ "@patternfly/react-table": "6.4.0-prerelease.2",
"clsx": "^2.1.1",
"react-jss": "^10.10.0"
},
@@ -44,8 +44,8 @@
},
"devDependencies": {
"@patternfly/documentation-framework": "^6.5.20",
- "@patternfly/patternfly": "^6.0.0",
- "@patternfly/react-code-editor": "^6.0.0",
+ "@patternfly/patternfly": "6.4.0-prerelease.1",
+ "@patternfly/react-code-editor": "6.4.0-prerelease.1",
"@patternfly/patternfly-a11y": "^5.1.0",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableResizableColumnsExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableResizableColumnsExample.tsx
new file mode 100644
index 00000000..211716e5
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableResizableColumnsExample.tsx
@@ -0,0 +1,155 @@
+import { FunctionComponent } from 'react';
+import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
+import { Button } from '@patternfly/react-core';
+import { ActionsColumn } from '@patternfly/react-table';
+
+interface Repository {
+ id: number;
+ name: string;
+ branches: string | null;
+ prs: string | null;
+ workspaces: string;
+ lastCommit: string;
+}
+
+const repositories: Repository[] = [
+ {
+ id: 1,
+ name: 'Repository one',
+ branches: 'Branch one',
+ prs: 'Pull request one',
+ workspaces: 'Workspace one',
+ lastCommit: 'Timestamp one'
+ },
+ {
+ id: 2,
+ name: 'Repository two',
+ branches: 'Branch two',
+ prs: 'Pull request two',
+ workspaces: 'Workspace two',
+ lastCommit: 'Timestamp two'
+ },
+ {
+ id: 3,
+ name: 'Repository three',
+ branches: 'Branch three',
+ prs: 'Pull request three',
+ workspaces: 'Workspace three',
+ lastCommit: 'Timestamp three'
+ },
+ {
+ id: 4,
+ name: 'Repository four',
+ branches: 'Branch four',
+ prs: 'Pull request four',
+ workspaces: 'Workspace four',
+ lastCommit: 'Timestamp four'
+ },
+ {
+ id: 5,
+ name: 'Repository five',
+ branches: 'Branch five',
+ prs: 'Pull request five',
+ workspaces: 'Workspace five',
+ lastCommit: 'Timestamp five'
+ },
+ {
+ id: 6,
+ name: 'Repository six',
+ branches: 'Branch six',
+ prs: 'Pull request six',
+ workspaces: 'Workspace six',
+ lastCommit: 'Timestamp six'
+ }
+];
+
+const rowActions = [
+ {
+ title: 'Some action',
+ onClick: () => console.log('clicked on Some action') // eslint-disable-line no-console
+ },
+ {
+ title:
Another action
,
+ onClick: () => console.log('clicked on Another action') // eslint-disable-line no-console
+ },
+ {
+ isSeparator: true
+ },
+ {
+ title: 'Third action',
+ onClick: () => console.log('clicked on Third action') // eslint-disable-line no-console
+ }
+];
+
+// you can also pass props to Tr by returning { row: DataViewTd[], props: TrProps } }
+const rows: DataViewTr[] = repositories.map(({ id, name, branches, prs, workspaces, lastCommit }) => [
+ { id, cell: workspaces, props: { favorites: { isFavorited: true } } },
+ {
+ cell: (
+
+ {name}
+
+ )
+ },
+ branches,
+ prs,
+ workspaces,
+ lastCommit,
+ { cell: , props: { isActionCell: true } }
+]);
+
+const ouiaId = 'TableExample';
+
+export const ResizableColumnsExample: FunctionComponent = () => {
+ const onResize = (
+ _e: React.MouseEvent | MouseEvent | React.KeyboardEvent | KeyboardEvent | TouchEvent,
+ id: string | number | undefined,
+ width: number
+ ) => {
+ // eslint-disable-next-line no-console
+ console.log(`resized column id: ${id} width to: ${width.toFixed(0)}px`);
+ };
+
+ const columns: DataViewTh[] = [
+ null,
+ 'Repositories',
+ {
+ cell: 'Branches',
+ resizableProps: {
+ isResizable: true,
+ onResize,
+ resizeButtonAriaLabel: 'Resize repositories column'
+ },
+ props: { id: 'repositories' }
+ },
+ {
+ cell: 'Pull requests',
+ resizableProps: {
+ isResizable: true,
+ onResize,
+ resizeButtonAriaLabel: 'Resize pull requests column'
+ },
+ props: { info: { tooltip: 'More information' }, id: 'pull-requests' }
+ },
+ {
+ cell: 'This is a really long title',
+ resizableProps: {
+ isResizable: true,
+ onResize,
+ resizeButtonAriaLabel: 'Resize this is a really long title column'
+ },
+ props: { info: { tooltip: 'More information' }, id: 'this-is-a-really-long-title' }
+ },
+ {
+ cell: 'Last commit',
+ resizableProps: {
+ isResizable: true,
+ onResize,
+ resizeButtonAriaLabel: 'Resize last commit column'
+ },
+ props: { sort: { sortBy: {}, columnIndex: 4 }, id: 'last-commit' }
+ }
+ ];
+
+ return ;
+};
diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
index b5c771c9..9ef6a7ce 100644
--- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
+++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
@@ -12,10 +12,19 @@ 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: 3
-propComponents: ['DataViewTableBasic', 'DataViewTableTree', 'DataViewTrTree', 'DataViewTrObject']
+propComponents:
+ [
+ 'DataViewTableBasic',
+ 'DataViewTableTree',
+ 'DataViewTrTree',
+ 'DataViewTrObject',
+ 'DataViewTh',
+ 'DataViewThResizableProps'
+ ]
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
---
-import { FunctionComponent, useMemo } from 'react';
+
+import { FunctionComponent, useMemo, useState } from 'react';
import { BrowserRouter, useSearchParams } from 'react-router-dom';
import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter } from '@patternfly/react-core';
import { CubesIcon, FolderIcon, FolderOpenIcon, LeafIcon, ExclamationCircleIcon } from '@patternfly/react-icons';
@@ -28,7 +37,9 @@ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynami
The **data view table** component renders your data into columns and rows within a [PatternFly table](/components/table) component. You can easily customize and configure the table with these additional [data view components and props](/extensions/data-view/table#props).
## Configuring rows and columns
+
To define rows and columns for your table, use these props:
+
- `columns`: Defines 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:
- `cell`: Content to display in the column head.
- `props` (optional): (`ThProps`) to pass to the `` component, such as `width`, `sort`, and other table head cell properties.
@@ -42,20 +53,38 @@ It is also possible to disable row selection using the `isSelectDisabled` functi
If you want to have all expandable nodes open on initial load pass the `expandAll` prop to the DataViewTable component
### Table example
+
```js file="./DataViewTableExample.tsx"
```
+### Resizable columns
+
+To allow a column to resize, add `isResizable` to the `DataViewTable` element, and pass `resizableProps` to each applicable header cell. The `resizableProps` object consists of the following fields:
+
+- `isResizable` - indicates that the column is resizable
+- `resizeButtonAriaLabel` - an accessible name for the resizable column's resize button. This must be passed in if the column is resizable.
+- `onResize` - a callback that will return the source event and the new width of the column
+- `width` - a default width value for a column
+- `minWidth` - the minimum width a column may shrink to
+- `increment` - how many pixels the column will move left or right for keyboard navigation
+- `shiftIncrement` - how many pixels the column will move left or right while shift is held for keyboard navigation
+- `screenReaderText` - text that will be announced when a column is resized
+
+```js file="./DataViewTableResizableColumnsExample.tsx"
+
+```
+
## Tree table
-A tree table includes expandable rows and custom icons for leaf and parent nodes.
+A tree table includes expandable rows and custom icons for leaf and parent nodes.
To enable a tree table, pass the `isTreeTable` flag to the `` component.
-
Tree table rows have to be defined with following keys:
- - `row`: Defines the content for each cell in the row.
- - `id`: Unique identifier for the row that's used for matching selected items.
- - `children` (optional): Defines the children rows.
+
+- `row`: Defines the content for each cell in the row.
+- `id`: Unique identifier for the row that's used for matching selected items.
+- `children` (optional): Defines the children rows.
To update a row's icon to reflect its expansion state, pass `collapsedIcon`, `expandedIcon`, and `leafIcon` to ``.
@@ -68,17 +97,21 @@ To disable row selection, pass the `isSelectDisabled` function to `selection` pr
```
## Sorting
+
The following example demonstrates how to enable sorting functionality within a data view. This implementation supports dynamic sorting by column and persists the sort state in the page's URL via [React Router](https://reactrouter.com/).
### Sorting example
+
```js file="./SortingExample.tsx"
```
+
### Sorting state
The `useDataViewSort` hook manages the sorting state of a data view and provides an easy way to handle sorting logic, such as synchronization with URL parameters and the definition of default sorting behavior.
**Initial values:**
+
- `initialSort` object to set default `sortBy` and `direction` values:
- `sortBy`: Key of the initial column to sort.
- `direction`: Default sorting direction (`asc` or `desc`).
@@ -88,44 +121,47 @@ The `useDataViewSort` hook manages the sorting state of a data view and provides
- Customizable parameter names for the URL:
- `sortByParam`: Name of the URL parameter for the column key.
- `directionParam`: Name of the URL parameter for the sorting direction.
-The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.
+ The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.
**Return values:**
+
- `sortBy`: Key of the column currently being sorted.
- `direction`: Current sorting direction (`asc` or `desc`).
- `onSort`: Function to handle sorting changes programmatically or via user interaction.
## States
-The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.
+The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.
### Empty
-When there is no data to render in the data view, you can instead display an empty state.
-You can create your empty state by passing a [PatternFly empty state](/components/empty-state) to the `empty` key of `headStates` or `bodyStates`.
+When there is no data to render in the data view, you can instead display an empty state.
+
+You can create your empty state by passing a [PatternFly empty state](/components/empty-state) to the `empty` key of `headStates` or `bodyStates`.
```js file="./DataViewTableEmptyExample.tsx"
```
### Error
+
When there is a data connection or retrieval error, you can display an error state.
The error state will be displayed when the data view `activeState` value is `error`.
-You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.
+You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.
```js file="./DataViewTableErrorExample.tsx"
```
### Loading
+
To indicate that data is loading, you can display a loading state.
The loading state will be displayed when the data view `activeState` value is `loading`.
-You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.
-
+You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.
```js file="./DataViewTableLoadingExample.tsx"
diff --git a/packages/module/patternfly-docs/generated/index.js b/packages/module/patternfly-docs/generated/index.js
index fb3f2d80..be42c242 100644
--- a/packages/module/patternfly-docs/generated/index.js
+++ b/packages/module/patternfly-docs/generated/index.js
@@ -14,8 +14,8 @@ module.exports = {
'/extensions/data-view/table/react': {
id: "Table",
title: "Data view table",
- toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
- examples: ["Table example","Tree table example","Sorting example","Empty","Error","Loading"],
+ toc: [{"text":"Configuring rows and columns"},[{"text":"Table example"},{"text":"Resizable columns"}],{"text":"Tree table"},[{"text":"Tree table example"}],{"text":"Sorting"},[{"text":"Sorting example"},{"text":"Sorting state"}],{"text":"States"},[{"text":"Empty"},{"text":"Error"},{"text":"Loading"}]],
+ examples: ["Table example","Resizable columns","Tree table example","Sorting example","Empty","Error","Loading"],
section: "extensions",
subsection: "Data view",
source: "react",
diff --git a/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap b/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap
index 1087ae19..344683fe 100644
--- a/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap
+++ b/packages/module/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap
@@ -135,7 +135,6 @@ exports[`DataViewCheckboxFilter component should render correctly 1`] = `
class="pf-v6-c-label__actions"
>
value != null && typeof value === 'object' && 'cell' in value;
+export type DataViewTh =
+ | ReactNode
+ | {
+ /** Table head cell node */
+ cell: ReactNode;
+ /** Additional props for a resizable column */
+ resizableProps?: DataViewThResizableProps;
+ /** Props passed to Th */
+ props?: ThProps;
+ };
+export const isDataViewThObject = (value: DataViewTh): value is { cell: ReactNode; props?: ThProps } =>
+ value != null && typeof value === 'object' && 'cell' in value;
// Basic table typings
export interface DataViewTrObject {
/** Array of rows */
- row: DataViewTd[],
+ row: DataViewTd[];
/** Unique identifier of a row */
- id?: string,
+ id?: string;
/** Props passed to Tr */
- props?: TrProps
+ props?: TrProps;
}
-export type DataViewTd = ReactNode | {
- /** Table body cell node */
- cell: ReactNode;
- /** Props passed to Td */
- props?: TdProps
-};
+export type DataViewTd =
+ | ReactNode
+ | {
+ /** Table body cell node */
+ cell: ReactNode;
+ /** Props passed to Td */
+ props?: TdProps;
+ };
export type DataViewTr = DataViewTd[] | DataViewTrObject;
-export const isDataViewTdObject = (value: DataViewTd): value is { cell: ReactNode; props?: TdProps } => value != null && typeof value === 'object' && 'cell' in value;
-export const isDataViewTrObject = (value: DataViewTr): value is { row: DataViewTd[], id?: string } => value != null && typeof value === 'object' && 'row' in value;
+export const isDataViewTdObject = (value: DataViewTd): value is { cell: ReactNode; props?: TdProps } =>
+ value != null && typeof value === 'object' && 'cell' in value;
+export const isDataViewTrObject = (value: DataViewTr): value is { row: DataViewTd[]; id?: string } =>
+ value != null && typeof value === 'object' && 'row' in value;
// Tree table typings
/** extends DataViewTrObject */
-export interface DataViewTrTree extends DataViewTrObject { id: string, children?: DataViewTrTree[] }
+export interface DataViewTrTree extends DataViewTrObject {
+ id: string;
+ children?: DataViewTrTree[];
+}
export type DataViewTableProps =
| ({
@@ -48,10 +57,22 @@ export type DataViewTableProps =
} & DataViewTableTreeProps)
| ({
isTreeTable?: false;
+ isResizable?: boolean;
} & DataViewTableBasicProps);
-export const DataViewTable: FC = (props) => (
- props.isTreeTable ? :
-);
+export const DataViewTable: FC = (props) => {
+ if (props.isTreeTable) {
+ return ;
+ } else {
+ const { isResizable, ...rest } = props;
+ return isResizable ? (
+
+
+
+ ) : (
+
+ );
+ }
+};
export default DataViewTable;
diff --git a/packages/module/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap b/packages/module/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap
index 2d46e784..074cb341 100644
--- a/packages/module/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap
+++ b/packages/module/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap
@@ -21,7 +21,7 @@ exports[`DataViewTable component should render a basic table correctly 1`] = `
data-ouia-safe="true"
>
>
/** Custom OUIA ID */
ouiaId?: string;
+ /** @hide Indicates if the table is resizable */
+ hasResizableColumns?: boolean;
}
export const DataViewTableBasic: FC = ({
@@ -31,6 +33,7 @@ export const DataViewTableBasic: FC = ({
ouiaId = 'DataViewTableBasic',
headStates,
bodyStates,
+ hasResizableColumns,
...props
}: DataViewTableBasicProps) => {
const { selection, activeState, isSelectable } = useInternalContext();
@@ -74,7 +77,7 @@ export const DataViewTableBasic: FC = ({
return (
- { activeHeadState || }
+ { activeHeadState || }
{ activeBodyState || {renderedRows} }
);
diff --git a/packages/module/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap b/packages/module/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap
index 2ccc2182..9c589c6a 100644
--- a/packages/module/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap
+++ b/packages/module/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap
@@ -21,7 +21,7 @@ exports[`DataViewTable component should render correctly 1`] = `
data-ouia-safe="true"
>
= ({
isTreeTable = false,
columns,
ouiaId = 'DataViewTableHead',
+ hasResizableColumns,
...props
}: DataViewTableHeadProps) => {
const { selection } = useInternalContext();
const { onSelect, isSelected } = selection ?? {};
- const cells = useMemo(() => [
- onSelect && isSelected && !isTreeTable ? (
-
- ) : null,
- ...columns.map((column, index) => (
-
- {isDataViewThObject(column) ? column.cell : column}
-
- )
- ) ], [ columns, ouiaId, onSelect, isSelected, isTreeTable ]);
+ const cells = useMemo(
+ () => [
+ onSelect && isSelected && !isTreeTable ? (
+
+ ) : null,
+ ...columns.map((column, index) => (
+
+ ))
+ ],
+ [ columns, ouiaId, onSelect, isSelected, isTreeTable, hasResizableColumns ]
+ );
return (
-
- {cells}
-
+ {cells}
);
};
diff --git a/packages/module/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap b/packages/module/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap
index 3d19db83..e113c84c 100644
--- a/packages/module/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap
+++ b/packages/module/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap
@@ -20,7 +20,7 @@ exports[`DataViewTableHead component should render correctly 1`] = `
data-ouia-safe="true"
>
(
+
+
+
+);
+
+const useStyles = createUseStyles({
+ dataViewResizeableTh: {
+ [tableCellPaddingInlineEnd.name]: `calc(${globalFontSizeBodyDefault.var} * 2)`
+ },
+ dataViewResizableButton: {
+ position: 'absolute',
+ insetInlineEnd: `calc(${globalFontSizeBodyDefault.var} / 2)`,
+ insetBlockEnd: tableCellPaddingBlockEnd.var,
+ cursor: 'grab',
+ '&:active': {
+ cursor: 'grabbing'
+ }
+ }
+});
+export interface DataViewThResizableProps {
+ /** Whether the column is resizable */
+ isResizable?: boolean;
+ /** 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. */
+ onResize?: (
+ event: ReactMouseEvent | MouseEvent | ReactKeyboardEvent | KeyboardEvent | TouchEvent,
+ id: string | number | undefined,
+ width: number
+ ) => void;
+ /** Width of the column */
+ width?: number;
+ /** Minimum width of the column */
+ minWidth?: number;
+ /** Increment for keyboard navigation */
+ increment?: number;
+ /** Increment for keyboard navigation while shift is held */
+ shiftIncrement?: number;
+ /** Provides an accessible name for the resizable column via a human readable string. */
+ resizeButtonAriaLabel?: string;
+ /** Screenreader text that gets announced when the column is resized. */
+ screenReaderText?: string;
+}
+export interface DataViewThProps {
+ /** Cell content */
+ content: ReactNode;
+ /** Resizable props */
+ resizableProps?: DataViewThResizableProps;
+ /** @hide Indicates whether the table has resizable columns */
+ hasResizableColumns?: boolean;
+ /** Props passed to Th */
+ thProps?: ThProps;
+}
+
+export const DataViewTh: FC = ({
+ content,
+ resizableProps = {},
+ hasResizableColumns = false,
+ thProps,
+ ...props
+}: DataViewThProps) => {
+ const thRef = useRef(null);
+
+ const [width, setWidth] = useState(resizableProps?.width ? resizableProps.width : 0);
+ // Tracks the current column width for the onResize callback, because the width state is not updated until after the resize is complete
+ const trackedWidth = useRef(0);
+ const classes = useStyles();
+
+ const isResizable = resizableProps?.isResizable || false;
+ const increment = resizableProps?.increment || 5;
+ const shiftIncrement = resizableProps?.shiftIncrement || 25;
+ const resizeButtonAriaLabel = resizableProps?.resizeButtonAriaLabel;
+ const onResize = resizableProps?.onResize || undefined;
+ const screenReaderText = resizableProps?.screenReaderText || `Column ${width.toFixed(0)} pixels`;
+ const dataViewThClassName = isResizable ? classes.dataViewResizeableTh : undefined;
+
+ const resizeButtonRef = useRef(null);
+ const setInitialVals = useRef(true);
+ const dragOffset = useRef(0);
+ const isResizing = useRef(false);
+ const isInView = useRef(true);
+
+ if (isResizable && !resizeButtonAriaLabel) {
+ // eslint-disable-next-line no-console
+ 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.'
+ );
+ }
+
+ useEffect(() => {
+ if (!isResizable) {
+ return;
+ }
+
+ const observed = resizeButtonRef.current;
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ isInView.current = entry.isIntersecting;
+ },
+ { threshold: 0.3 }
+ );
+
+ if (observed) {
+ observer.observe(observed);
+ }
+
+ return () => {
+ if (observed) {
+ observer.unobserve(observed);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ if ((isResizable || hasResizableColumns) && setInitialVals.current && width === 0) {
+ setWidth(thRef.current?.getBoundingClientRect().width || 0);
+ setInitialVals.current = false;
+ }
+ }, [isResizable, hasResizableColumns, setInitialVals]);
+
+ const setDragOffset = (e: ReactMouseEvent | ReactTouchEvent) => {
+ const isRTL = getLanguageDirection(thRef.current as HTMLElement) === 'rtl';
+ const startingMousePos = 'clientX' in e ? e.clientX : e.touches[0].clientX;
+ const startingColumnPos =
+ (isRTL ? thRef.current?.getBoundingClientRect().left : thRef.current?.getBoundingClientRect().right) || 0;
+
+ dragOffset.current = startingColumnPos - startingMousePos;
+ };
+
+ const handleMousedown = (e: ReactMouseEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ document.addEventListener('mousemove', callbackMouseMove);
+ document.addEventListener('mouseup', callbackMouseUp);
+
+ // When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
+ if (dragOffset.current === 0) {
+ setDragOffset(e);
+ }
+
+ isResizing.current = true;
+ };
+
+ const handleMouseMove = (e: MouseEvent) => {
+ const mousePos = e.clientX;
+
+ handleControlMove(e, dragOffset.current + mousePos);
+ };
+
+ const handleTouchStart = (e: ReactTouchEvent) => {
+ e.stopPropagation();
+ document.addEventListener('touchmove', callbackTouchMove, { passive: false });
+ document.addEventListener('touchend', callbackTouchEnd);
+
+ // When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
+ if (dragOffset.current === 0) {
+ setDragOffset(e);
+ }
+
+ isResizing.current = true;
+ };
+
+ const handleTouchMove = (e: TouchEvent) => {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ const touchPos = e.touches[0].clientX;
+
+ handleControlMove(e, touchPos);
+ };
+
+ const handleControlMove = (e: MouseEvent | TouchEvent, controlPosition: number) => {
+ e.stopPropagation();
+
+ if (!isResizing.current) {
+ return;
+ }
+
+ const columnRect = thRef.current?.getBoundingClientRect();
+ if (columnRect === undefined) {
+ return;
+ }
+
+ const mousePos = controlPosition;
+ const isRTL = getLanguageDirection(thRef.current as HTMLElement) === 'rtl';
+ let newSize = isRTL ? columnRect?.right - mousePos : mousePos - columnRect?.left;
+
+ // Prevent the column from shrinking below a specified minimum width
+ if (resizableProps.minWidth && newSize < resizableProps.minWidth) {
+ newSize = resizableProps.minWidth;
+ }
+
+ thRef.current?.style.setProperty('min-width', newSize + 'px');
+ trackedWidth.current = newSize;
+ setWidth(newSize);
+ };
+
+ const handleMouseup = (e: MouseEvent) => {
+ if (!isResizing.current) {
+ return;
+ }
+ // Reset the isResizing and dragOffset values to their initial states
+ isResizing.current = false;
+ dragOffset.current = 0;
+
+ // Call the onResize callback with the new width
+ onResize && onResize(e, thProps?.id, trackedWidth.current);
+
+ // Handle scroll into view when column drag button is moved off screen
+ if (resizeButtonRef.current && !isInView.current) {
+ resizeButtonRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
+ }
+
+ document.removeEventListener('mousemove', callbackMouseMove);
+ document.removeEventListener('mouseup', callbackMouseUp);
+ };
+
+ const handleTouchEnd = (e: TouchEvent) => {
+ e.stopPropagation();
+ if (!isResizing) {
+ return;
+ }
+ // Reset the isResizing and dragOffset values to their initial states
+ isResizing.current = false;
+ dragOffset.current = 0;
+
+ // Call the onResize callback with the new width
+ onResize && onResize(e, thProps?.id, trackedWidth.current);
+
+ document.removeEventListener('touchmove', callbackTouchMove);
+ document.removeEventListener('touchend', callbackTouchEnd);
+ };
+
+ const callbackMouseMove = useCallback(handleMouseMove, []);
+ const callbackTouchEnd = useCallback(handleTouchEnd, []);
+ const callbackTouchMove = useCallback(handleTouchMove, []);
+ const callbackMouseUp = useCallback(handleMouseup, []);
+
+ const handleKeys = (e: ReactKeyboardEvent) => {
+ const key = e.key;
+
+ if (key === 'Tab') {
+ isResizing.current = false;
+ }
+
+ if (key !== 'ArrowLeft' && key !== 'ArrowRight') {
+ return;
+ }
+ e.preventDefault();
+
+ isResizing.current = true;
+ const isRTL = getLanguageDirection(thRef.current as HTMLElement) === 'rtl';
+ const columnRect = thRef.current?.getBoundingClientRect();
+
+ let newSize = columnRect?.width || 0;
+ let delta = 0;
+ const _increment = e.shiftKey ? shiftIncrement : increment;
+
+ if (key === 'ArrowRight') {
+ if (isRTL) {
+ delta = -_increment;
+ } else {
+ delta = _increment;
+ }
+ } else if (key === 'ArrowLeft') {
+ if (isRTL) {
+ delta = _increment;
+ } else {
+ delta = -_increment;
+ }
+ }
+ newSize = newSize + delta;
+
+ thRef.current?.style.setProperty('min-width', newSize + 'px');
+ setWidth(newSize);
+ onResize && onResize(e, thProps?.id, newSize);
+ };
+
+ const resizableContent = (
+
+
+ {screenReaderText}
+
+ }
+ onMouseDown={handleMousedown}
+ onKeyDown={handleKeys}
+ onTouchStart={handleTouchStart}
+ aria-label={resizeButtonAriaLabel}
+ className={classes.dataViewResizableButton}
+ />
+
+ );
+
+ return (
+ 0 ? { minWidth: width } : undefined}
+ ref={thRef}
+ modifier="truncate"
+ className={dataViewThClassName}
+ {...(isResizable && { additionalContent: resizableContent })}
+ >
+ {content}
+
+ );
+};
+
+export default DataViewTh;
diff --git a/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap b/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap
index c7fef596..ad2980eb 100644
--- a/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap
+++ b/packages/module/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap
@@ -106,7 +106,6 @@ exports[`DataViewToolbar component should render correctly 1`] = `
class="pf-v6-c-pagination__nav-control pf-m-first"
>