diff --git a/package.json b/package.json
index 1ddbb6d63ae..bd2f42601d8 100644
--- a/package.json
+++ b/package.json
@@ -67,12 +67,13 @@
"typescript": "^4.0.0"
},
"scripts": {
- "build": "yarn build:generate && yarn build:esm && yarn build:cjs",
+ "build": "yarn build:generate && yarn build:esm && yarn build:subpaths && yarn build:cjs",
"build:cjs": "tsc --build --verbose packages/tsconfig.cjs.json",
"build:esm": "tsc --build --verbose packages/tsconfig.json",
"build:integration": "lerna run build:demo-app --stream",
"build:docs": "yarn workspace @patternfly/react-docs build:docs",
"build:generate": "lerna run generate --parallel --stream",
+ "build:subpaths": "lerna run subpaths --parallel --stream",
"build:umd": "lerna run build:umd --parallel --stream",
"clean": "yarn clean:build && lerna run clean --parallel",
"clean:build": "rimraf .cache .eslintcache coverage",
diff --git a/packages/react-catalog-view-extension/CHANGELOG.md b/packages/react-catalog-view-extension/CHANGELOG.md
index 16c88669d75..af7db5c53ab 100644
--- a/packages/react-catalog-view-extension/CHANGELOG.md
+++ b/packages/react-catalog-view-extension/CHANGELOG.md
@@ -3,6 +3,217 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [4.93.15](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.14...@patternfly/react-catalog-view-extension@4.93.15) (2022-12-12)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.14](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.13...@patternfly/react-catalog-view-extension@4.93.14) (2022-12-09)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.13](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.12...@patternfly/react-catalog-view-extension@4.93.13) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.12](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.11...@patternfly/react-catalog-view-extension@4.93.12) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.93.11 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.93.10 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.9](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.8...@patternfly/react-catalog-view-extension@4.93.9) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.8](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.7...@patternfly/react-catalog-view-extension@4.93.8) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.7](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.6...@patternfly/react-catalog-view-extension@4.93.7) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.6](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.5...@patternfly/react-catalog-view-extension@4.93.6) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.5](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.4...@patternfly/react-catalog-view-extension@4.93.5) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.93.4 (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.93.3](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.93.2...@patternfly/react-catalog-view-extension@4.93.3) (2022-12-05)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.93.2 (2022-12-01)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.93.1 (2022-11-30)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+# [4.93.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.66...@patternfly/react-catalog-view-extension@4.93.0) (2022-11-16)
+
+
+### Features
+
+* **Tabs:** add TabAction, update core ver ([#8348](https://github.com/patternfly/patternfly-react/issues/8348)) ([8c584b4](https://github.com/patternfly/patternfly-react/commit/8c584b48f8e545cf226f785b873ee11eda505724))
+
+
+
+
+
+## [4.92.66](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.65...@patternfly/react-catalog-view-extension@4.92.66) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.65](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.64...@patternfly/react-catalog-view-extension@4.92.65) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.64](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.63...@patternfly/react-catalog-view-extension@4.92.64) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.63](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.62...@patternfly/react-catalog-view-extension@4.92.63) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.62](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.61...@patternfly/react-catalog-view-extension@4.92.62) (2022-11-08)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.61](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.60...@patternfly/react-catalog-view-extension@4.92.61) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.60](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.59...@patternfly/react-catalog-view-extension@4.92.60) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.59](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.58...@patternfly/react-catalog-view-extension@4.92.59) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## [4.92.58](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-catalog-view-extension@4.92.57...@patternfly/react-catalog-view-extension@4.92.58) (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
+## 4.92.57 (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-catalog-view-extension
+
+
+
+
+
## 4.92.56 (2022-11-01)
**Note:** Version bump only for package @patternfly/react-catalog-view-extension
diff --git a/packages/react-catalog-view-extension/package.json b/packages/react-catalog-view-extension/package.json
index 7ff0dcf5d8e..b4142acdc08 100644
--- a/packages/react-catalog-view-extension/package.json
+++ b/packages/react-catalog-view-extension/package.json
@@ -1,6 +1,6 @@
{
"name": "@patternfly/react-catalog-view-extension",
- "version": "4.92.56",
+ "version": "4.93.15",
"description": "This library provides catalog view extensions for PatternFly 4 React.",
"main": "dist/js/index.js",
"module": "dist/esm/index.js",
@@ -35,9 +35,9 @@
"clean": "rimraf dist"
},
"dependencies": {
- "@patternfly/patternfly": "4.219.2",
- "@patternfly/react-core": "^4.258.4",
- "@patternfly/react-styles": "^4.91.10"
+ "@patternfly/patternfly": "4.222.4",
+ "@patternfly/react-core": "^4.267.6",
+ "@patternfly/react-styles": "^4.92.3"
},
"devDependencies": {
"rimraf": "^2.6.2",
diff --git a/packages/react-charts/CHANGELOG.md b/packages/react-charts/CHANGELOG.md
index 584406ded7a..798a196939e 100644
--- a/packages/react-charts/CHANGELOG.md
+++ b/packages/react-charts/CHANGELOG.md
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## 6.94.15 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-charts
+
+
+
+
+
+## 6.94.14 (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-charts
+
+
+
+
+
+## 6.94.13 (2022-12-01)
+
+**Note:** Version bump only for package @patternfly/react-charts
+
+
+
+
+
+## 6.94.12 (2022-11-16)
+
+**Note:** Version bump only for package @patternfly/react-charts
+
+
+
+
+
## 6.94.11 (2022-10-27)
**Note:** Version bump only for package @patternfly/react-charts
diff --git a/packages/react-charts/package.json b/packages/react-charts/package.json
index 3774b5b7b6e..f16da9ebb39 100644
--- a/packages/react-charts/package.json
+++ b/packages/react-charts/package.json
@@ -1,6 +1,6 @@
{
"name": "@patternfly/react-charts",
- "version": "6.94.11",
+ "version": "6.94.15",
"description": "This library provides a set of React chart components for use with the PatternFly reference implementation.",
"main": "dist/js/index.js",
"module": "dist/esm/index.js",
@@ -29,8 +29,8 @@
},
"homepage": "https://github.com/patternfly/patternfly-react#readme",
"dependencies": {
- "@patternfly/react-styles": "^4.91.10",
- "@patternfly/react-tokens": "^4.93.10",
+ "@patternfly/react-styles": "^4.92.3",
+ "@patternfly/react-tokens": "^4.94.3",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.19",
"tslib": "^2.0.0",
diff --git a/packages/react-code-editor/CHANGELOG.md b/packages/react-code-editor/CHANGELOG.md
index be86cf43e69..9b5988aa4e7 100644
--- a/packages/react-code-editor/CHANGELOG.md
+++ b/packages/react-code-editor/CHANGELOG.md
@@ -3,6 +3,214 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [4.82.82](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.81...@patternfly/react-code-editor@4.82.82) (2022-12-12)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.81](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.80...@patternfly/react-code-editor@4.82.81) (2022-12-09)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.80](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.79...@patternfly/react-code-editor@4.82.80) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.79](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.78...@patternfly/react-code-editor@4.82.79) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.78 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.77 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.76](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.75...@patternfly/react-code-editor@4.82.76) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.75](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.74...@patternfly/react-code-editor@4.82.75) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.74](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.73...@patternfly/react-code-editor@4.82.74) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.73](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.72...@patternfly/react-code-editor@4.82.73) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.72](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.71...@patternfly/react-code-editor@4.82.72) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.71 (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.70](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.69...@patternfly/react-code-editor@4.82.70) (2022-12-05)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.69 (2022-12-01)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.68 (2022-11-30)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.67](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.66...@patternfly/react-code-editor@4.82.67) (2022-11-16)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.66](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.65...@patternfly/react-code-editor@4.82.66) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.65](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.64...@patternfly/react-code-editor@4.82.65) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.64](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.63...@patternfly/react-code-editor@4.82.64) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.63](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.62...@patternfly/react-code-editor@4.82.63) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.62](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.61...@patternfly/react-code-editor@4.82.62) (2022-11-08)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.61](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.60...@patternfly/react-code-editor@4.82.61) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.60](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.59...@patternfly/react-code-editor@4.82.60) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.59](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.58...@patternfly/react-code-editor@4.82.59) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## [4.82.58](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-code-editor@4.82.57...@patternfly/react-code-editor@4.82.58) (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
+## 4.82.57 (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-code-editor
+
+
+
+
+
## 4.82.56 (2022-11-01)
**Note:** Version bump only for package @patternfly/react-code-editor
diff --git a/packages/react-code-editor/README.md b/packages/react-code-editor/README.md
index 58f1a3b39a7..8910e0c0eb6 100644
--- a/packages/react-code-editor/README.md
+++ b/packages/react-code-editor/README.md
@@ -53,3 +53,62 @@ Install peer deps
```
To properly install the library `monaco-editor-webpack-plugin` be sure to follow the [plugin instructions](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin)
+
+#### With create-react-app Projects
+If you created your project with `create-react-app` you'll have some extra work to do, or you wont have syntax highlighting. Using the webpack plugin requires updating your webpack config, which `create-react-app` abstracts away. You can `npm eject` your project, but you may not want to do that. To keep your app set up in the `create-react-app` style but to get access to your webpack config you can use `react-app-rewired`.
+
+First, install `react-app-rewired` as a development dependency:
+```sh
+$ npm install -D react-app-rewired
+```
+
+Next, replace all of the `react-script` references in your `package.json` `scripts` section with `react-app-required`:
+```json
+ "scripts": {
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
+ "test": "react-app-rewired test",
+ "eject": "react-app-rewired eject"
+ }
+```
+
+Next, create a `config-overries.js` file at the root of your project and add the following:
+
+```javascript
+const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
+
+module.exports = function override(config, env) {
+ config.plugins.push(new MonacoWebpackPlugin({
+ languages: ['json', 'yaml', 'shell']
+ }));
+ return config;
+}
+```
+
+Note: You should change the `languages` array based on your needs.
+
+You can now start your app with `npm start` and syntax highlighting should work.
+
+#### Enable YAML Syntax Highlighting
+The Monaco editor doesn't ship with full YAML support. You can configure your code editor with `Languages.yaml` but there will be no highlighting, even i you have the webpack plugin working correctly. To enable YAML support you need to do the following:
+
+First, install `monaco-yaml`:
+```shell
+$ npm install --save monaco-yaml
+```
+
+Next, at the entrypoint of your app enable it:
+```javascript
+import { setDiagnosticsOptions } from 'monaco-yaml';
+
+setDiagnosticsOptions({
+ enableSchemaRequest: true,
+ hover: true,
+ completion: true,
+ validate: true,
+ format: true,
+ schemas: [],
+});
+```
+
+The `monaco-yaml` plugin has a lot of options so check out their docs to see what else you may want to add.
diff --git a/packages/react-code-editor/package.json b/packages/react-code-editor/package.json
index d33482b9fa3..aee33f6a98d 100644
--- a/packages/react-code-editor/package.json
+++ b/packages/react-code-editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@patternfly/react-code-editor",
- "version": "4.82.56",
+ "version": "4.82.82",
"description": "This package provides a PatternFly wrapper for the Monaco code editor\n",
"main": "dist/js/index.js",
"module": "dist/esm/index.js",
@@ -30,9 +30,9 @@
"clean": "rimraf dist"
},
"dependencies": {
- "@patternfly/react-core": "^4.258.4",
- "@patternfly/react-icons": "^4.92.10",
- "@patternfly/react-styles": "^4.91.10",
+ "@patternfly/react-core": "^4.267.6",
+ "@patternfly/react-icons": "^4.93.3",
+ "@patternfly/react-styles": "^4.92.3",
"react-dropzone": "14.2.3",
"tslib": "^2.0.0"
},
diff --git a/packages/react-console/CHANGELOG.md b/packages/react-console/CHANGELOG.md
index 2599c42388b..f47121eee20 100644
--- a/packages/react-console/CHANGELOG.md
+++ b/packages/react-console/CHANGELOG.md
@@ -3,6 +3,220 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [4.93.15](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.14...@patternfly/react-console@4.93.15) (2022-12-12)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.14](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.13...@patternfly/react-console@4.93.14) (2022-12-09)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.13](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.12...@patternfly/react-console@4.93.13) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.12](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.11...@patternfly/react-console@4.93.12) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.93.11 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.93.10 (2022-12-08)
+
+
+### Bug Fixes
+
+* **VncConsole:** prevent running initialization code more than once ([#8373](https://github.com/patternfly/patternfly-react/issues/8373)) ([#8374](https://github.com/patternfly/patternfly-react/issues/8374)) ([285c5ff](https://github.com/patternfly/patternfly-react/commit/285c5ff54f165c43752f36107db2db7473cc338a))
+
+
+
+
+
+## [4.93.9](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.8...@patternfly/react-console@4.93.9) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.8](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.7...@patternfly/react-console@4.93.8) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.7](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.6...@patternfly/react-console@4.93.7) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.6](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.5...@patternfly/react-console@4.93.6) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.5](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.4...@patternfly/react-console@4.93.5) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.93.4 (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.93.3](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.93.2...@patternfly/react-console@4.93.3) (2022-12-05)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.93.2 (2022-12-01)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.93.1 (2022-11-30)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+# [4.93.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.66...@patternfly/react-console@4.93.0) (2022-11-16)
+
+
+### Features
+
+* **Tabs:** add TabAction, update core ver ([#8348](https://github.com/patternfly/patternfly-react/issues/8348)) ([8c584b4](https://github.com/patternfly/patternfly-react/commit/8c584b48f8e545cf226f785b873ee11eda505724))
+
+
+
+
+
+## [4.92.66](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.65...@patternfly/react-console@4.92.66) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.65](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.64...@patternfly/react-console@4.92.65) (2022-11-15)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.64](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.63...@patternfly/react-console@4.92.64) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.63](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.62...@patternfly/react-console@4.92.63) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.62](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.61...@patternfly/react-console@4.92.62) (2022-11-08)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.61](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.60...@patternfly/react-console@4.92.61) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.60](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.59...@patternfly/react-console@4.92.60) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.59](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.58...@patternfly/react-console@4.92.59) (2022-11-07)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## [4.92.58](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-console@4.92.57...@patternfly/react-console@4.92.58) (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
+## 4.92.57 (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-console
+
+
+
+
+
## 4.92.56 (2022-11-01)
**Note:** Version bump only for package @patternfly/react-console
diff --git a/packages/react-console/package.json b/packages/react-console/package.json
index 9a1cf67391d..99eb7675bfa 100644
--- a/packages/react-console/package.json
+++ b/packages/react-console/package.json
@@ -1,6 +1,6 @@
{
"name": "@patternfly/react-console",
- "version": "4.92.56",
+ "version": "4.93.15",
"description": "This package provides VncConsole, SerialConsole and DesktopViewer React components to be used alongside patternfly-react to access virtual machine or server consoles.",
"main": "dist/js/index.js",
"module": "dist/esm/index.js",
@@ -33,8 +33,8 @@
},
"dependencies": {
"@novnc/novnc": "^1.2.0",
- "@patternfly/patternfly": "4.219.2",
- "@patternfly/react-core": "^4.258.4",
+ "@patternfly/patternfly": "4.222.4",
+ "@patternfly/react-core": "^4.267.6",
"@spice-project/spice-html5": "^0.2.1",
"@types/file-saver": "^2.0.1",
"file-saver": "^1.3.8",
diff --git a/packages/react-console/src/components/VncConsole/VncConsole.tsx b/packages/react-console/src/components/VncConsole/VncConsole.tsx
index c8ac73999e2..89355de0686 100644
--- a/packages/react-console/src/components/VncConsole/VncConsole.tsx
+++ b/packages/react-console/src/components/VncConsole/VncConsole.tsx
@@ -92,28 +92,53 @@ export const VncConsole: React.FunctionComponent = ({
textCtrlAltDel
}) => {
const rfb = React.useRef();
- let novncStaticComponent: React.ReactNode;
- let novncElem: HTMLDivElement;
+ const novncElem = React.useRef(null);
const [status, setStatus] = React.useState(CONNECTING);
- const addEventListeners = () => {
+ const onConnected = () => {
+ setStatus(CONNECTED);
+ };
+
+ const _onDisconnected = React.useCallback(
+ (e: any) => {
+ setStatus(DISCONNECTED);
+ onDisconnected(e);
+ },
+ [onDisconnected]
+ );
+
+ const _onSecurityFailure = React.useCallback(
+ (e: any) => {
+ setStatus(DISCONNECTED);
+ onSecurityFailure(e);
+ },
+ [onSecurityFailure]
+ );
+
+ const onCtrlAltDel = () => {
+ if (rfb.current) {
+ rfb?.current?.sendCtrlAltDel();
+ }
+ };
+
+ const addEventListeners = React.useCallback(() => {
if (rfb.current) {
rfb.current?.addEventListener('connect', onConnected);
rfb.current?.addEventListener('disconnect', _onDisconnected);
rfb.current?.addEventListener('securityfailure', _onSecurityFailure);
}
- };
+ }, [rfb, _onDisconnected, _onSecurityFailure]);
- const removeEventListeners = () => {
+ const removeEventListeners = React.useCallback(() => {
if (rfb.current) {
rfb.current.removeEventListener('connect', onConnected);
rfb.current.removeEventListener('disconnect', _onDisconnected);
rfb.current.removeEventListener('securityfailure', _onSecurityFailure);
}
- };
+ }, [rfb, _onDisconnected, _onSecurityFailure]);
- const connect = () => {
+ const connect = React.useCallback(() => {
const protocol = encrypt ? 'wss' : 'ws';
const url = `${protocol}://${host}:${port}/${path}`;
@@ -122,12 +147,25 @@ export const VncConsole: React.FunctionComponent = ({
shared,
credentials
};
- rfb.current = new RFB(novncElem, url, options);
+ rfb.current = new RFB(novncElem.current, url, options);
addEventListeners();
rfb.current.viewOnly = viewOnly;
rfb.current.scaleViewport = scaleViewport;
rfb.current.resizeSession = resizeSession;
- };
+ }, [
+ addEventListeners,
+ host,
+ path,
+ port,
+ resizeSession,
+ scaleViewport,
+ viewOnly,
+ encrypt,
+ rfb,
+ repeaterID,
+ shared,
+ credentials
+ ]);
React.useEffect(() => {
initLogging(vncLogging);
@@ -152,26 +190,6 @@ export const VncConsole: React.FunctionComponent = ({
rfb.current.disconnect();
};
- const onConnected = () => {
- setStatus(CONNECTED);
- };
-
- const _onDisconnected = (e: any) => {
- setStatus(DISCONNECTED);
- onDisconnected(e);
- };
-
- const _onSecurityFailure = (e: any) => {
- setStatus(DISCONNECTED);
- onSecurityFailure(e);
- };
-
- const onCtrlAltDel = () => {
- if (rfb.current) {
- rfb?.current?.sendCtrlAltDel();
- }
- };
-
let rightContent;
let emptyState;
switch (status) {
@@ -207,10 +225,6 @@ export const VncConsole: React.FunctionComponent = ({
);
}
- if (!novncStaticComponent) {
- novncStaticComponent = (novncElem = e)} />;
- }
-
return (
<>
{rightContent}
@@ -219,7 +233,7 @@ export const VncConsole: React.FunctionComponent
= ({
{emptyState}
- {novncStaticComponent}
+
diff --git a/packages/react-core/.gitignore b/packages/react-core/.gitignore
new file mode 100644
index 00000000000..77cfc55e590
--- /dev/null
+++ b/packages/react-core/.gitignore
@@ -0,0 +1,5 @@
+/next
+/deprecated
+/components
+/layouts
+/helpers
\ No newline at end of file
diff --git a/packages/react-core/CHANGELOG.md b/packages/react-core/CHANGELOG.md
index 4dcd6a8d067..e39310420d3 100644
--- a/packages/react-core/CHANGELOG.md
+++ b/packages/react-core/CHANGELOG.md
@@ -3,6 +3,275 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [4.267.6](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.267.5...@patternfly/react-core@4.267.6) (2022-12-12)
+
+
+### Bug Fixes
+
+* Remove package exports and replace subpaths script ([#8438](https://github.com/patternfly/patternfly-react/issues/8438)) ([75bea27](https://github.com/patternfly/patternfly-react/commit/75bea2790cffcfe2ffc9ca46d82748e3e85674fc))
+
+
+
+
+
+## [4.267.5](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.267.4...@patternfly/react-core@4.267.5) (2022-12-09)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+## [4.267.4](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.267.3...@patternfly/react-core@4.267.4) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+## [4.267.3](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.267.2...@patternfly/react-core@4.267.3) (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+## 4.267.2 (2022-12-08)
+
+
+### Bug Fixes
+
+* **Menu:** removed li wrapper from breadcrumb example ([#8433](https://github.com/patternfly/patternfly-react/issues/8433)) ([1f41a71](https://github.com/patternfly/patternfly-react/commit/1f41a712fb8124a931277eb6611923746cd24496))
+
+
+
+
+
+## 4.267.1 (2022-12-08)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+# [4.267.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.266.3...@patternfly/react-core@4.267.0) (2022-12-07)
+
+
+### Features
+
+* **Menu:** add drilldown filter demo, add flag to support demo & fix some keyboard interaction ([#8405](https://github.com/patternfly/patternfly-react/issues/8405)) ([6fd28da](https://github.com/patternfly/patternfly-react/commit/6fd28da87bbc1f06553df388c626de8d94e0fa05))
+
+
+
+
+
+## [4.266.3](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.266.2...@patternfly/react-core@4.266.3) (2022-12-07)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+## [4.266.2](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.266.1...@patternfly/react-core@4.266.2) (2022-12-06)
+
+
+### Bug Fixes
+
+* **Pagination:** Added support for insets ([#8412](https://github.com/patternfly/patternfly-react/issues/8412)) ([1053d53](https://github.com/patternfly/patternfly-react/commit/1053d530605c4f629eefdc7501b121a1906394a2))
+
+
+
+
+
+## [4.266.1](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.266.0...@patternfly/react-core@4.266.1) (2022-12-06)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+# [4.266.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.265.2...@patternfly/react-core@4.266.0) (2022-12-06)
+
+
+### Features
+
+* **ClipboardCopy, Truncate:** add removeFindDomNode ([#8371](https://github.com/patternfly/patternfly-react/issues/8371)) ([1cb5801](https://github.com/patternfly/patternfly-react/commit/1cb580101a2a4dcb1d29a8ba46ba96f9a9912de3))
+
+
+
+
+
+## 4.265.2 (2022-12-06)
+
+
+### Bug Fixes
+
+* **AdvancedSearchMenu:** allow spaces in search form field values ([#8372](https://github.com/patternfly/patternfly-react/issues/8372)) ([e026b8c](https://github.com/patternfly/patternfly-react/commit/e026b8c04fe25e94c5d7c4b697d774bab562eed8)), closes [#8369](https://github.com/patternfly/patternfly-react/issues/8369)
+
+
+
+
+
+## [4.265.1](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.265.0...@patternfly/react-core@4.265.1) (2022-12-05)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+# 4.265.0 (2022-12-01)
+
+
+### Bug Fixes
+
+* **Dropdown next:** Added itemId to DropdownItem props ([#8356](https://github.com/patternfly/patternfly-react/issues/8356)) ([17f3c4b](https://github.com/patternfly/patternfly-react/commit/17f3c4b4f2c8cb4fc3914729a83dbe8cc5258744))
+* **Menu:** updated breadcrumb drilldown example ([#8385](https://github.com/patternfly/patternfly-react/issues/8385)) ([ca6e8d4](https://github.com/patternfly/patternfly-react/commit/ca6e8d4eaeec06e85dd11f639dab3f0c94882025))
+* **TimePicker:** removed redundant onBlur ([#8366](https://github.com/patternfly/patternfly-react/issues/8366)) ([4a5a202](https://github.com/patternfly/patternfly-react/commit/4a5a202f26b36e9b9d4555b3c9c49fdc9c07b3fa))
+
+
+### Features
+
+* add pf-screen-reader for screen reader text to Badge ([#8361](https://github.com/patternfly/patternfly-react/issues/8361)) ([91f6b4d](https://github.com/patternfly/patternfly-react/commit/91f6b4d034a179361f2ec24cac169c14aa8c5195)), closes [#8354](https://github.com/patternfly/patternfly-react/issues/8354)
+
+
+
+
+
+## 4.264.1 (2022-11-30)
+
+
+### Bug Fixes
+
+* react-core subpaths for next/deprecated modules ([#8341](https://github.com/patternfly/patternfly-react/issues/8341)) ([ed002d2](https://github.com/patternfly/patternfly-react/commit/ed002d204d12f9e138a7373ce0e7318153fbe502))
+
+
+
+
+
+# [4.264.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.263.0...@patternfly/react-core@4.264.0) (2022-11-16)
+
+
+### Features
+
+* **Tabs:** add TabAction, update core ver ([#8348](https://github.com/patternfly/patternfly-react/issues/8348)) ([8c584b4](https://github.com/patternfly/patternfly-react/commit/8c584b48f8e545cf226f785b873ee11eda505724))
+
+
+
+
+
+# [4.263.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.262.0...@patternfly/react-core@4.263.0) (2022-11-15)
+
+
+### Bug Fixes
+
+* **NumberInput:** allow user to back out number to type input ([#8304](https://github.com/patternfly/patternfly-react/issues/8304)) ([7dda7a9](https://github.com/patternfly/patternfly-react/commit/7dda7a9419f020df3f9ea2468ee35d2c28f16382))
+* **Popper:** add display contents to wrapping divs ([#8317](https://github.com/patternfly/patternfly-react/issues/8317)) ([1851a2c](https://github.com/patternfly/patternfly-react/commit/1851a2cd33a7a032e63c156a9e6c52653862bd9b))
+
+
+### Features
+
+* **spinner:** added isInline, updated link button ([#8328](https://github.com/patternfly/patternfly-react/issues/8328)) ([29f76e1](https://github.com/patternfly/patternfly-react/commit/29f76e1b1f99df446a8d1c5719a0d3fa6790ac9a))
+
+
+
+
+
+# [4.262.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.261.0...@patternfly/react-core@4.262.0) (2022-11-15)
+
+
+### Features
+
+* **MenuInput:** use SearchInput instead of TextInput ([#8329](https://github.com/patternfly/patternfly-react/issues/8329)) ([e00b995](https://github.com/patternfly/patternfly-react/commit/e00b995b927c76d4128d5584ff0ed0d97f672c9a))
+
+
+
+
+
+# [4.261.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.260.1...@patternfly/react-core@4.261.0) (2022-11-09)
+
+
+### Features
+
+* **MultipleFileUpload:** added support for helper text ([#8344](https://github.com/patternfly/patternfly-react/issues/8344)) ([f8faf8b](https://github.com/patternfly/patternfly-react/commit/f8faf8bf62b8ece44f5283922711730921fcd537))
+
+
+
+
+
+## [4.260.1](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.260.0...@patternfly/react-core@4.260.1) (2022-11-09)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+# [4.260.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.259.0...@patternfly/react-core@4.260.0) (2022-11-08)
+
+
+### Features
+
+* **Progress:** added helper text ([#8307](https://github.com/patternfly/patternfly-react/issues/8307)) ([7ad9398](https://github.com/patternfly/patternfly-react/commit/7ad9398588c8d7f0da5ec5c6a6bdeb58d2308d39))
+
+
+
+
+
+# [4.259.0](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.258.8...@patternfly/react-core@4.259.0) (2022-11-07)
+
+
+### Features
+
+* **Popper, misc:** allow components to customize popper z-index ([#8310](https://github.com/patternfly/patternfly-react/issues/8310)) ([17dff4c](https://github.com/patternfly/patternfly-react/commit/17dff4c3bde667f426994a1e9073c9c58b9c598a))
+
+
+
+
+
+## [4.258.8](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.258.7...@patternfly/react-core@4.258.8) (2022-11-07)
+
+
+### Bug Fixes
+
+* **bulk-select:** fixed toggle spacing ([#8326](https://github.com/patternfly/patternfly-react/issues/8326)) ([fe40f19](https://github.com/patternfly/patternfly-react/commit/fe40f19058a383cfc12e3f7a07d07af8dc579d04))
+
+
+
+
+
+## [4.258.7](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.258.6...@patternfly/react-core@4.258.7) (2022-11-07)
+
+
+### Bug Fixes
+
+* **TimePicker:** fixed bugs when updating time/minTime/maxTime props ([#8267](https://github.com/patternfly/patternfly-react/issues/8267)) ([f1408df](https://github.com/patternfly/patternfly-react/commit/f1408df627115592f95576f01c072e52a6c7d9e8))
+
+
+
+
+
+## [4.258.6](https://github.com/patternfly/patternfly-react/compare/@patternfly/react-core@4.258.5...@patternfly/react-core@4.258.6) (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
+## 4.258.5 (2022-11-04)
+
+**Note:** Version bump only for package @patternfly/react-core
+
+
+
+
+
## 4.258.4 (2022-11-01)
**Note:** Version bump only for package @patternfly/react-core
diff --git a/packages/react-core/package.json b/packages/react-core/package.json
index 0574aea9438..883ff68d302 100644
--- a/packages/react-core/package.json
+++ b/packages/react-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@patternfly/react-core",
- "version": "4.258.4",
+ "version": "4.267.6",
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
"main": "dist/js/index.js",
"module": "dist/esm/index.js",
@@ -41,21 +41,22 @@
"scripts": {
"build:umd": "rollup -c --environment IS_PRODUCTION",
"clean": "rimraf dist",
- "generate": "node scripts/copyStyles.js"
+ "generate": "node scripts/copyStyles.js",
+ "subpaths": "node scripts/copySubpaths.js"
},
"dependencies": {
- "@patternfly/react-icons": "^4.92.10",
- "@patternfly/react-styles": "^4.91.10",
- "@patternfly/react-tokens": "^4.93.10",
+ "@patternfly/react-icons": "^4.93.3",
+ "@patternfly/react-styles": "^4.92.3",
+ "@patternfly/react-tokens": "^4.94.3",
"focus-trap": "6.9.2",
"react-dropzone": "^14.2.3",
"tippy.js": "5.1.2",
"tslib": "^2.0.0"
},
"devDependencies": {
- "@patternfly/patternfly": "4.219.2",
+ "@patternfly/patternfly": "4.222.4",
"@rollup/plugin-commonjs": "^21.0.0",
- "@rollup/plugin-node-resolve": "^13.0.0",
+ "@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-replace": "^3.0.0",
"css": "^2.2.3",
"fs-extra": "^6.0.1",
diff --git a/packages/react-core/scripts/copySubpaths.js b/packages/react-core/scripts/copySubpaths.js
new file mode 100644
index 00000000000..cffb00dd7ce
--- /dev/null
+++ b/packages/react-core/scripts/copySubpaths.js
@@ -0,0 +1,12 @@
+/**
+ * Copy subpath modules into the package root directory for ease of access.
+ */
+const { copySync } = require('fs-extra');
+const { resolve, dirname } = require('path');
+
+['next', 'deprecated', 'components', 'layouts', 'helpers'].forEach(subPathName => {
+ const source = dirname(require.resolve(`@patternfly/react-core/dist/esm/${subPathName}`));
+ const destination = resolve(__dirname, `../${subPathName}`);
+
+ copySync(source, destination);
+});
diff --git a/packages/react-core/src/components/Alert/__tests__/AlertActionCloseButton.test.tsx b/packages/react-core/src/components/Alert/__tests__/AlertActionCloseButton.test.tsx
index be395f8e64a..0116fe2ac09 100644
--- a/packages/react-core/src/components/Alert/__tests__/AlertActionCloseButton.test.tsx
+++ b/packages/react-core/src/components/Alert/__tests__/AlertActionCloseButton.test.tsx
@@ -5,18 +5,7 @@ import userEvent from '@testing-library/user-event';
import { AlertActionCloseButton } from '../AlertActionCloseButton';
import { AlertContext } from '../AlertContext';
-jest.mock('../../Button', () => ({
- Button: ({ children, variant, isInline, onClick, ...props }) => (
- <>
-
- {children}
-
- {`variant: ${variant}`}
- Test label
- >
- ),
- ButtonVariant: { plain: 'plain' }
-}));
+jest.mock('../../Button');
test('Renders without children', () => {
render(
@@ -98,7 +87,7 @@ test('Renders with an aria label composed with the title and variantLabel provid
test('Renders with an aria label composed with the title provided via a context and variantLabel provided via prop', () => {
render(
-
+
);
@@ -106,14 +95,14 @@ test('Renders with an aria label composed with the title provided via a context
});
test('Renders with the aria label provided via prop when one is provided', () => {
- render(
-
-
-
- );
-
- expect(screen.getByRole('button')).toHaveAccessibleName('Aria label prop');
- });
+ render(
+
+
+
+ );
+
+ expect(screen.getByRole('button')).toHaveAccessibleName('Aria label prop');
+});
test('Matches the snapshot', () => {
const { asFragment } = render(
diff --git a/packages/react-core/src/components/Alert/__tests__/AlertActionLink.test.tsx b/packages/react-core/src/components/Alert/__tests__/AlertActionLink.test.tsx
index 9fccb898171..49f06bd1cce 100644
--- a/packages/react-core/src/components/Alert/__tests__/AlertActionLink.test.tsx
+++ b/packages/react-core/src/components/Alert/__tests__/AlertActionLink.test.tsx
@@ -2,16 +2,7 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import { AlertActionLink } from '../AlertActionLink';
-jest.mock('../../Button', () => ({
- Button: ({ children, variant, isInline, ...props }) => (
- <>
- {children}
- {`variant: ${variant}`}
- {`isInline: ${isInline}`}
- >
- ),
- ButtonVariant: { link: 'link' }
-}));
+jest.mock('../../Button');
test('Renders without children', () => {
render(
diff --git a/packages/react-core/src/components/Alert/__tests__/AlertToggleExpandButton.test.tsx b/packages/react-core/src/components/Alert/__tests__/AlertToggleExpandButton.test.tsx
index 8ae6e321253..0d7d3d3c6a3 100644
--- a/packages/react-core/src/components/Alert/__tests__/AlertToggleExpandButton.test.tsx
+++ b/packages/react-core/src/components/Alert/__tests__/AlertToggleExpandButton.test.tsx
@@ -5,18 +5,7 @@ import userEvent from '@testing-library/user-event';
import { AlertToggleExpandButton } from '../AlertToggleExpandButton';
import { AlertContext } from '../AlertContext';
-jest.mock('../../Button', () => ({
- Button: ({ children, variant, isInline, onClick, ...props }) => (
- <>
-
- {children}
-
- {`variant: ${variant}`}
- Test label
- >
- ),
- ButtonVariant: { plain: 'plain' }
-}));
+jest.mock('../../Button');
jest.mock('@patternfly/react-icons/dist/esm/icons/angle-right-icon', () => () => 'Icon mock');
diff --git a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionCloseButton.test.tsx.snap b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionCloseButton.test.tsx.snap
index 82f1e3a7d65..c88f18237d9 100644
--- a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionCloseButton.test.tsx.snap
+++ b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionCloseButton.test.tsx.snap
@@ -27,5 +27,14 @@ exports[`Matches the snapshot 1`] = `
>
Test label
+
+ isInline: undefined
+
+
+ iconPosition: undefined
+
+
`;
diff --git a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionLink.test.tsx.snap b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionLink.test.tsx.snap
index fd53fe19fee..0def8dd9fd6 100644
--- a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionLink.test.tsx.snap
+++ b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertActionLink.test.tsx.snap
@@ -10,8 +10,19 @@ exports[`Matches the snapshot 1`] = `
variant: link
+
+ Test label
+
isInline: true
+
+ iconPosition: undefined
+
+
`;
diff --git a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertToggleExpandButton.test.tsx.snap b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertToggleExpandButton.test.tsx.snap
index c1a42f67acc..f447c81e299 100644
--- a/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertToggleExpandButton.test.tsx.snap
+++ b/packages/react-core/src/components/Alert/__tests__/__snapshots__/AlertToggleExpandButton.test.tsx.snap
@@ -20,5 +20,14 @@ exports[`Matches snapshot 1`] = `
>
Test label
+
+ isInline: undefined
+
+
+ iconPosition: undefined
+
+
`;
diff --git a/packages/react-core/src/components/ApplicationLauncher/ApplicationLauncher.tsx b/packages/react-core/src/components/ApplicationLauncher/ApplicationLauncher.tsx
index 54bbc7525f0..53fbd0a250f 100644
--- a/packages/react-core/src/components/ApplicationLauncher/ApplicationLauncher.tsx
+++ b/packages/react-core/src/components/ApplicationLauncher/ApplicationLauncher.tsx
@@ -58,6 +58,10 @@ export interface ApplicationLauncherProps extends React.HTMLProps {
@@ -120,6 +124,8 @@ export class ApplicationLauncher extends React.Component ({
- Button: ({ variant, iconPosition, children, icon }) => (
- <>
- {children}
- {variant}
- {iconPosition}
- {icon}
- >
- )
-}));
+jest.mock('../../Button');
test('Renders BackToTop', () => {
render(
@@ -138,13 +129,13 @@ test('Passes correct text content to button child component', () => {
test('Passes correct variant to button child component', () => {
render( );
- expect(screen.getByText('primary')).toBeVisible();
+ expect(screen.getByText('variant: primary')).toBeVisible();
});
test('Passes correct iconPosition to button child component', () => {
render( );
- expect(screen.getByText('right')).toBeVisible();
+ expect(screen.getByText('iconPosition: right')).toBeVisible();
});
test('Passes correct icon to button child component', () => {
diff --git a/packages/react-core/src/components/BackToTop/__tests__/__snapshots__/BackToTop.test.tsx.snap b/packages/react-core/src/components/BackToTop/__tests__/__snapshots__/BackToTop.test.tsx.snap
index 85d5e50d7e5..84cec57ed52 100644
--- a/packages/react-core/src/components/BackToTop/__tests__/__snapshots__/BackToTop.test.tsx.snap
+++ b/packages/react-core/src/components/BackToTop/__tests__/__snapshots__/BackToTop.test.tsx.snap
@@ -9,10 +9,18 @@ exports[`Matches the snapshot 1`] = `
Back to top
- primary
+ variant: primary
+
+
+ Test label
+
+
+ isInline: undefined
- right
+ iconPosition: right
{
+test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('backdrop').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(
Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders with the pf-c-backdrop', () => {
+ render(
Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-backdrop');
+});
+
+test('Renders with only the class pf-c-backdrop by default', () => {
+ render(
Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-backdrop', { exact: true });
+});
+
+test('Renders with custom class name when className prop is passed', () => {
+ render(
Test );
+ expect(screen.getByText('Test')).toHaveClass('test-class');
+});
+
+test('Renders with the inherited element props spread to the component', () => {
+ render(
Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('this is a simple backdrop');
+});
+
+test('Matches the snapshot', () => {
const { asFragment } = render(
Backdrop );
expect(asFragment()).toMatchSnapshot();
});
diff --git a/packages/react-core/src/components/Backdrop/__tests__/__snapshots__/Backdrop.test.tsx.snap b/packages/react-core/src/components/Backdrop/__tests__/__snapshots__/Backdrop.test.tsx.snap
index c341a6f53f4..c714bc285ec 100644
--- a/packages/react-core/src/components/Backdrop/__tests__/__snapshots__/Backdrop.test.tsx.snap
+++ b/packages/react-core/src/components/Backdrop/__tests__/__snapshots__/Backdrop.test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Backdrop Test 1`] = `
+exports[`Matches the snapshot 1`] = `
{
+ /** Text announced by screen readers to indicate the current content/status of the badge. */
+ screenReaderText?: string;
/** Adds styling to the badge to indicate it has been read */
isRead?: boolean;
/** content rendered inside the Badge */
@@ -15,6 +17,7 @@ export const Badge: React.FunctionComponent
= ({
isRead = false,
className = '',
children = '',
+ screenReaderText,
...props
}: BadgeProps) => (
= ({
className={css(styles.badge, (isRead ? styles.modifiers.read : styles.modifiers.unread) as any, className)}
>
{children}
+ {screenReaderText && {screenReaderText} }
);
Badge.displayName = 'Badge';
diff --git a/packages/react-core/src/components/Badge/__tests__/Badge.test.tsx b/packages/react-core/src/components/Badge/__tests__/Badge.test.tsx
index f2f679c014d..5ffefc19975 100644
--- a/packages/react-core/src/components/Badge/__tests__/Badge.test.tsx
+++ b/packages/react-core/src/components/Badge/__tests__/Badge.test.tsx
@@ -31,6 +31,16 @@ test('Renders with class name pf-m-read when isRead prop is true', () => {
expect(screen.getByText('Test')).toHaveClass('pf-m-read');
});
+test('Does not render pf-screen-reader class by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toContainHTML(' ');
+});
+
+test('Renders screenReaderText passed via prop', () => {
+ render(Test );
+ expect(screen.getByText('Custom screen reader text')).toBeInTheDocument();
+});
+
test('Renders with custom class name when className prop is provided', () => {
render(Test );
expect(screen.getByText('Test')).toHaveClass('custom-class');
diff --git a/packages/react-core/src/components/Badge/examples/BadgeUnread.tsx b/packages/react-core/src/components/Badge/examples/BadgeUnread.tsx
index 19c2e4bb3cf..b5bd85255a9 100644
--- a/packages/react-core/src/components/Badge/examples/BadgeUnread.tsx
+++ b/packages/react-core/src/components/Badge/examples/BadgeUnread.tsx
@@ -3,6 +3,17 @@ import { Badge } from '@patternfly/react-core';
export const BadgeUnread: React.FunctionComponent = () => (
- 7 24 240 999+
+
+ 7
+
+
+ 24
+
+
+ 240
+
+
+ 999+
+
);
diff --git a/packages/react-core/src/components/Button/Button.tsx b/packages/react-core/src/components/Button/Button.tsx
index 7cfdb8fe470..9a1e5099797 100644
--- a/packages/react-core/src/components/Button/Button.tsx
+++ b/packages/react-core/src/components/Button/Button.tsx
@@ -175,6 +175,7 @@ const ButtonBase: React.FunctionComponent = ({
(
+ <>
+
+ {children}
+
+ {`variant: ${variant}`}
+ Test label
+ {`isInline: ${isInline}`}
+ {`iconPosition: ${iconPosition}`}
+ {icon}
+ >
+);
+
+export const ButtonVariant = { plain: 'plain', link: 'link' };
diff --git a/packages/react-core/src/components/Button/__mocks__/index.ts b/packages/react-core/src/components/Button/__mocks__/index.ts
new file mode 100644
index 00000000000..8b166a86e4d
--- /dev/null
+++ b/packages/react-core/src/components/Button/__mocks__/index.ts
@@ -0,0 +1 @@
+export * from './Button';
diff --git a/packages/react-core/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap b/packages/react-core/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap
index 79ce3c4478f..596c17e9380 100644
--- a/packages/react-core/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap
+++ b/packages/react-core/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap
@@ -240,7 +240,7 @@ exports[`Button isLoading inline link 1`] = `
diff --git a/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx b/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
index de0e55165c9..996a2f88d12 100644
--- a/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
+++ b/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
@@ -20,6 +20,15 @@ export enum Weekday {
Saturday
}
+export interface CalendarMonthInlineProps {
+ /** Component wrapping the calendar month when used inline. Recommended to be 'article'. */
+ component?: keyof JSX.IntrinsicElements;
+ /** Title of the calendar rendered above the inline calendar month. Recommended to be a 'title' component. */
+ title?: React.ReactNode;
+ /** Id of the accessible label of the calendar month. Recommended to map to the title. */
+ ariaLabelledby?: string;
+}
+
/** Additional properties that extend from and can be passed to the main component. These
* properties allow customizing the calendar formatting and aria-labels.
*/
@@ -49,6 +58,8 @@ export interface CalendarFormat {
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | Weekday;
/** Accessible label for the year input. */
yearInputAriaLabel?: string;
+ /** Props used to ensure accessibility when displaying the calendar month inline. */
+ inlineProps?: CalendarMonthInlineProps;
}
export interface CalendarProps extends CalendarFormat, Omit, 'onChange'> {
@@ -133,6 +144,7 @@ export const CalendarMonth = ({
yearInputAriaLabel = 'Select year',
cellAriaLabel,
isDateFocused = false,
+ inlineProps,
...props
}: CalendarProps) => {
const longMonths = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(monthNum => new Date(1990, monthNum)).map(monthFormat);
@@ -232,7 +244,8 @@ export const CalendarMonth = ({
const isHoveredDateValid = isValidated(hoveredDate);
const monthFormatted = monthFormat(focusedDate);
const yearFormatted = yearFormat(focusedDate);
- return (
+
+ const calendarToRender = (
@@ -386,4 +399,16 @@ export const CalendarMonth = ({
);
+
+ if (inlineProps !== undefined) {
+ const Component = (inlineProps.component ? inlineProps.component : 'article') as any;
+ return (
+
+ {inlineProps.title}
+ {calendarToRender}
+
+ );
+ }
+ return calendarToRender;
};
+CalendarMonth.displayName = 'CalendarMonth';
diff --git a/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx b/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx
index a5d5214408e..4b70968618e 100644
--- a/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx
+++ b/packages/react-core/src/components/CalendarMonth/__tests__/CalendarMonth.test.tsx
@@ -55,3 +55,12 @@ test('Next year dates have correct year in aria label', () => {
const nextYearDate = screen.getByRole('button', { name: '1 January 2025' });
expect(nextYearDate).toBeVisible();
});
+
+test('InlineProps render correct wrapper component and attributes', () => {
+ render(
Title , ariaLabelledby: "hi"}} />);
+
+ const article = screen.getByRole('article');
+ expect(article).toHaveAttribute('aria-labelledby', 'hi');
+ const title = screen.getByText('Title');
+ expect(title).toBeVisible();
+});
diff --git a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonth.md b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonth.md
index 993a87a1501..0c3b27f6938 100644
--- a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonth.md
+++ b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonth.md
@@ -2,7 +2,7 @@
id: Calendar month
section: components
cssPrefix: pf-c-calendar-month
-propComponents: ['CalendarMonth', 'CalendarFormat']
+propComponents: ['CalendarMonth', 'CalendarFormat', 'CalendarMonthInlineProps']
---
## Examples
diff --git a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthDateRange.tsx b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthDateRange.tsx
index 1afd9cdbbc9..1bdfd79ea46 100644
--- a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthDateRange.tsx
+++ b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthDateRange.tsx
@@ -1,10 +1,27 @@
import React from 'react';
-import { CalendarMonth } from '@patternfly/react-core';
+import { CalendarMonth, Title, CalendarMonthInlineProps } from '@patternfly/react-core';
export const CalendarMonthDateRange: React.FunctionComponent = () => {
const startDate = new Date(2020, 10, 11);
const endDate = new Date(2020, 10, 24);
const disablePreStartDates = (date: Date) => date >= startDate;
- return
;
+ const inlineProps: CalendarMonthInlineProps = {
+ component: 'article',
+ title: (
+
+ Calendar month displaying a range
+
+ ),
+ ariaLabelledby: 'display-range'
+ };
+
+ return (
+
+ );
};
diff --git a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthSelectableDate.tsx b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthSelectableDate.tsx
index d61f03d889c..f9fe55fd318 100644
--- a/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthSelectableDate.tsx
+++ b/packages/react-core/src/components/CalendarMonth/examples/CalendarMonthSelectableDate.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { CalendarMonth } from '@patternfly/react-core';
+import { CalendarMonth, Title, CalendarMonthInlineProps } from '@patternfly/react-core';
export const CalendarMonthSelectableDate: React.FunctionComponent = () => {
const [date, setDate] = React.useState(new Date(2020, 10, 24));
@@ -9,10 +9,20 @@ export const CalendarMonthSelectableDate: React.FunctionComponent = () => {
console.log(`updated month: ${newDate.getMonth()}, updated year: ${newDate.getFullYear()}`);
};
+ const inlineProps: CalendarMonthInlineProps = {
+ component: 'article',
+ title: (
+
+ Select your favorite date
+
+ ),
+ ariaLabelledby: 'favorite-date'
+ };
+
return (
-
+ <>
+
Selected date: {date.toString()}
-
-
+ >
);
};
diff --git a/packages/react-core/src/components/ContextSelector/ContextSelector.tsx b/packages/react-core/src/components/ContextSelector/ContextSelector.tsx
index 834b9097399..b0c2227e17e 100644
--- a/packages/react-core/src/components/ContextSelector/ContextSelector.tsx
+++ b/packages/react-core/src/components/ContextSelector/ContextSelector.tsx
@@ -61,6 +61,8 @@ export interface ContextSelectorProps extends OUIAProps {
isFlipEnabled?: boolean;
/** Id of the context selector */
id?: string;
+ /** z-index of the context selector when menuAppendTo is not inline. */
+ zIndex?: number;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -88,7 +90,8 @@ export class ContextSelector extends React.Component
);
}
diff --git a/packages/react-core/src/components/Dropdown/Dropdown.tsx b/packages/react-core/src/components/Dropdown/Dropdown.tsx
index 8014f93cb49..c7a162b3768 100644
--- a/packages/react-core/src/components/Dropdown/Dropdown.tsx
+++ b/packages/react-core/src/components/Dropdown/Dropdown.tsx
@@ -56,6 +56,10 @@ export interface DropdownProps extends React.HTMLProps
, OUIAProp
* appended inline, e.g. `menuAppendTo="parent"`
*/
isFlipEnabled?: boolean;
+ /** @beta Opt-in for updated popper that does not use findDOMNode. */
+ removeFindDomNode?: boolean;
+ /** z-index of the dropdown when menuAppendTo is not inline. */
+ zIndex?: number;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -72,6 +76,8 @@ export const Dropdown: React.FunctionComponent = ({
contextProps,
menuAppendTo = 'inline',
isFlipEnabled = true,
+ removeFindDomNode = false,
+ zIndex = 9999,
...props
}: DropdownProps) => (
= ({
...contextProps
}}
>
-
+
);
Dropdown.displayName = 'Dropdown';
diff --git a/packages/react-core/src/components/Dropdown/DropdownWithContext.tsx b/packages/react-core/src/components/Dropdown/DropdownWithContext.tsx
index 29c8600d1bc..aaebd987c4b 100644
--- a/packages/react-core/src/components/Dropdown/DropdownWithContext.tsx
+++ b/packages/react-core/src/components/Dropdown/DropdownWithContext.tsx
@@ -77,6 +77,7 @@ export class DropdownWithContext extends React.Component
);
diff --git a/packages/react-core/src/components/HelperText/__mocks__/HelperText.tsx b/packages/react-core/src/components/HelperText/__mocks__/HelperText.tsx
new file mode 100644
index 00000000000..070adc10f9c
--- /dev/null
+++ b/packages/react-core/src/components/HelperText/__mocks__/HelperText.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { HelperTextProps } from '../';
+
+export const HelperText = ({ children, isLiveRegion }: HelperTextProps) => (
+ <>
+ {children}
+ {`isLiveRegion: ${isLiveRegion}`}
+ >
+);
diff --git a/packages/react-core/src/components/HelperText/__mocks__/HelperTextItem.tsx b/packages/react-core/src/components/HelperText/__mocks__/HelperTextItem.tsx
new file mode 100644
index 00000000000..c431a78864c
--- /dev/null
+++ b/packages/react-core/src/components/HelperText/__mocks__/HelperTextItem.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { HelperTextItemProps } from '../';
+
+export const HelperTextItem = ({ children, variant }: HelperTextItemProps) => (
+ <>
+ {children}
+ {`variant: ${variant}`}
+ >
+);
diff --git a/packages/react-core/src/components/HelperText/__mocks__/index.ts b/packages/react-core/src/components/HelperText/__mocks__/index.ts
new file mode 100644
index 00000000000..c35042a2c6b
--- /dev/null
+++ b/packages/react-core/src/components/HelperText/__mocks__/index.ts
@@ -0,0 +1,2 @@
+export * from './HelperText';
+export * from './HelperTextItem';
diff --git a/packages/react-core/src/components/Label/examples/LabelCompact.tsx b/packages/react-core/src/components/Label/examples/LabelCompact.tsx
index 2f77a0644a7..a0567cae904 100644
--- a/packages/react-core/src/components/Label/examples/LabelCompact.tsx
+++ b/packages/react-core/src/components/Label/examples/LabelCompact.tsx
@@ -14,10 +14,10 @@ export const LabelCompact: React.FunctionComponent = () => (
} onClose={() => Function.prototype}>
Compact icon removable
{' '}
-
+
Compact link
{' '}
- Function.prototype}>
+ Function.prototype}>
Compact link removable
} onClose={() => Function.prototype} isTruncated>
diff --git a/packages/react-core/src/components/Menu/Menu.tsx b/packages/react-core/src/components/Menu/Menu.tsx
index 01eb36031fe..c3de96009a9 100644
--- a/packages/react-core/src/components/Menu/Menu.tsx
+++ b/packages/react-core/src/components/Menu/Menu.tsx
@@ -71,6 +71,7 @@ export interface MenuState {
transitionMoveTarget: HTMLElement;
flyoutRef: React.Ref | null;
disableHover: boolean;
+ currentDrilldownMenuId: string;
}
class MenuBase extends React.Component {
@@ -98,7 +99,8 @@ class MenuBase extends React.Component {
searchInputValue: '',
transitionMoveTarget: null,
flyoutRef: null,
- disableHover: false
+ disableHover: false,
+ currentDrilldownMenuId: this.props.id
};
allowTabFirstItem() {
@@ -155,9 +157,19 @@ class MenuBase extends React.Component {
this.setState({ transitionMoveTarget: null });
} else {
const nextMenu = current.querySelector('#' + this.props.activeMenu) || current || null;
- const nextTarget = Array.from(nextMenu.getElementsByTagName('UL')[0].children).filter(
+ const nextMenuChildren = Array.from(nextMenu.getElementsByTagName('UL')[0].children);
+
+ if (!this.state.currentDrilldownMenuId || nextMenu.id !== this.state.currentDrilldownMenuId) {
+ this.setState({ currentDrilldownMenuId: nextMenu.id });
+ } else {
+ // if the drilldown transition ends on the same menu, do not focus the first item
+ return;
+ }
+
+ const nextTarget = nextMenuChildren.filter(
el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider'))
)[0].firstChild;
+
(nextTarget as HTMLElement).focus();
(nextTarget as HTMLElement).tabIndex = 0;
}
@@ -284,12 +296,16 @@ class MenuBase extends React.Component {
additionalKeyHandler={this.handleExtraKeys}
createNavigableElements={this.createNavigableElements}
isActiveElement={(element: Element) =>
- document.activeElement.closest('li') === element ||
+ document.activeElement.closest('li') === element || // if element is a basic MenuItem
document.activeElement.parentElement === element ||
+ document.activeElement.closest('.pf-c-menu__search') === element || // if element is a MenuInput
(document.activeElement.closest('ol') && document.activeElement.closest('ol').firstChild === element)
}
getFocusableElement={(navigableElement: Element) =>
- navigableElement.querySelector('input') || (navigableElement.firstChild as Element)
+ (navigableElement.tagName === 'DIV' && navigableElement.querySelector('input')) || // for MenuInput
+ ((navigableElement.firstChild as Element).tagName === 'LABEL' &&
+ navigableElement.querySelector('input')) || // for MenuItem checkboxes
+ (navigableElement.firstChild as Element)
}
noHorizontalArrowHandling={
document.activeElement &&
diff --git a/packages/react-core/src/components/Menu/MenuItem.tsx b/packages/react-core/src/components/Menu/MenuItem.tsx
index 1af964b3850..484ab70a90a 100644
--- a/packages/react-core/src/components/Menu/MenuItem.tsx
+++ b/packages/react-core/src/components/Menu/MenuItem.tsx
@@ -22,7 +22,7 @@ export interface MenuItemProps extends Omit, 'onC
className?: string;
/** Identifies the component in the Menu onSelect or onActionClick callback */
itemId?: any;
- /** Target navigation link */
+ /** Target navigation link. Should not be used if the flyout prop is defined. */
to?: string;
/** @beta Flag indicating the item has a checkbox */
hasCheck?: boolean;
@@ -54,7 +54,7 @@ export interface MenuItemProps extends Omit, 'onC
isFocused?: boolean;
/** Flag indicating the item is in danger state */
isDanger?: boolean;
- /** @beta Flyout menu */
+ /** @beta Flyout menu. Should not be used if the to prop is defined. */
flyoutMenu?: React.ReactElement;
/** @beta Callback function when mouse leaves trigger */
onShowFlyout?: (event?: any) => void;
@@ -202,6 +202,7 @@ const MenuItemBase: React.FunctionComponent = ({
if (key === ' ' || key === 'Enter' || key === 'ArrowRight' || type === 'click') {
event.stopPropagation();
+ event.preventDefault();
if (!flyoutVisible) {
showFlyout(true);
setFlyoutTarget(target as HTMLElement);
diff --git a/packages/react-core/src/components/Menu/examples/Menu.md b/packages/react-core/src/components/Menu/examples/Menu.md
index cc8142926d2..47a7199bdf4 100644
--- a/packages/react-core/src/components/Menu/examples/Menu.md
+++ b/packages/react-core/src/components/Menu/examples/Menu.md
@@ -38,9 +38,9 @@ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-ico
```ts file="./MenuWithCheckbox.tsx"
```
-### Filtering with text input
+### Filtering with search input
-```ts file="MenuFilteringWithTextInput.tsx"
+```ts file="MenuFilteringWithSearchInput.tsx"
```
### With links
@@ -105,6 +105,11 @@ To render an initially drilled in menu, the `menuDrilledIn`, `drilldownPath`, an
```ts file="MenuWithDrilldownBreadcrumbs.tsx" isBeta
```
+### With drilldown and inline filter
+
+```ts file="MenuFilterDrilldown.tsx"
+```
+
### Scrollable
```ts file="MenuScrollable.tsx"
diff --git a/packages/react-core/src/components/Menu/examples/MenuFilterDrilldown.tsx b/packages/react-core/src/components/Menu/examples/MenuFilterDrilldown.tsx
new file mode 100644
index 00000000000..e22dab9b11c
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuFilterDrilldown.tsx
@@ -0,0 +1,123 @@
+import React from 'react';
+import {
+ Menu,
+ MenuContent,
+ MenuList,
+ MenuItem,
+ Divider,
+ DrilldownMenu,
+ MenuInput,
+ SearchInput
+} from '@patternfly/react-core';
+
+export const MenuWithDrilldown: React.FunctionComponent = () => {
+ const [menuDrilledIn, setMenuDrilledIn] = React.useState([]);
+ const [drilldownPath, setDrilldownPath] = React.useState([]);
+ const [menuHeights, setMenuHeights] = React.useState({});
+ const [activeMenu, setActiveMenu] = React.useState('filter_drilldown-rootMenu');
+
+ const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
+ setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
+ setDrilldownPath([...drilldownPath, pathId]);
+ setActiveMenu(toMenuId);
+ };
+
+ const drillOut = (toMenuId: string) => {
+ const menuDrilledInSansLast = menuDrilledIn.slice(0, menuDrilledIn.length - 1);
+ const pathSansLast = drilldownPath.slice(0, drilldownPath.length - 1);
+ setMenuDrilledIn(menuDrilledInSansLast);
+ setDrilldownPath(pathSansLast);
+ setActiveMenu(toMenuId);
+ };
+
+ const setHeight = (menuId: string, height: number) => {
+ if (
+ menuHeights[menuId] === undefined ||
+ (menuId !== 'filter_drilldown-rootMenu' && menuHeights[menuId] !== height)
+ ) {
+ setMenuHeights({ ...menuHeights, [menuId]: height });
+ }
+ };
+
+ const searchRef = React.createRef();
+ const [startInput, setStartInput] = React.useState('');
+
+ const handleStartTextInputChange = (value: string) => {
+ setStartInput(value);
+ searchRef?.current?.focus();
+ };
+
+ const startDrillItems = [
+ {
+ item: 'Application grouping',
+ rest: { description: 'Description text' }
+ },
+ { item: 'Labels' },
+ { item: 'Annotations' },
+ { item: 'Count' },
+ { item: 'Count 2' },
+ { item: 'Count 3' },
+ { item: 'Other' }
+ ];
+
+ const mapped = startDrillItems
+ .filter(opt => !startInput || opt.item.toLowerCase().includes(startInput.toString().toLowerCase()))
+ .map((opt, index) => (
+
+ {opt.item}
+
+ ));
+ if (startInput && mapped.length === 0) {
+ mapped.push(
+
+ No results found
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx b/packages/react-core/src/components/Menu/examples/MenuFilteringWithSearchInput.tsx
similarity index 85%
rename from packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx
rename to packages/react-core/src/components/Menu/examples/MenuFilteringWithSearchInput.tsx
index 4e8f32e2453..6571d536663 100644
--- a/packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx
+++ b/packages/react-core/src/components/Menu/examples/MenuFilteringWithSearchInput.tsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { Menu, MenuList, MenuItem, MenuContent, MenuInput, TextInput, Divider } from '@patternfly/react-core';
+import { Menu, MenuList, MenuItem, MenuContent, MenuInput, Divider, SearchInput } from '@patternfly/react-core';
-export const MenuFilteringWithTextInput: React.FunctionComponent = () => {
+export const MenuFilteringWithSearchInput: React.FunctionComponent = () => {
const [activeItem, setActiveItem] = React.useState(0);
const [input, setInput] = React.useState('');
@@ -35,10 +35,9 @@ export const MenuFilteringWithTextInput: React.FunctionComponent = () => {
return (
- handleTextInputChange(value)}
/>
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx
index 528c33ba9c8..ef0b6fe0774 100644
--- a/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx
+++ b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx
@@ -26,7 +26,7 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
const [drilldownPath, setDrilldownPath] = React.useState([]);
const [menuHeights, setMenuHeights] = React.useState({});
const [activeMenu, setActiveMenu] = React.useState('breadcrumbs-rootMenu');
- const [breadcrumb, setBreadcrumb] = React.useState();
+ const [breadcrumb, setBreadcrumb] = React.useState();
const [withMaxMenuHeight, setWithMaxMenuHeight] = React.useState(false);
const onToggle = (isOpen: boolean, key: string) => {
@@ -37,6 +37,12 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
case 'label':
setBreadcrumb(labelsBreadcrumb(isOpen));
break;
+ case 'pause-app':
+ setBreadcrumb(pauseRolloutsAppGrpBreadcrumb(isOpen));
+ break;
+ case 'pause-label':
+ setBreadcrumb(pauseRolloutsLabelsBreadcrumb(isOpen));
+ break;
default:
break;
}
@@ -46,14 +52,17 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
setWithMaxMenuHeight(checked);
};
- const drillOut = (toMenuId: string, fromPathId: string, _breadcrumb: JSX.Element | null) => {
- const indexOfMenuId = menuDrilledIn.indexOf(toMenuId);
- const menuDrilledInSansLast = menuDrilledIn.slice(0, indexOfMenuId);
- const indexOfMenuIdPath = drilldownPath.indexOf(fromPathId);
- const pathSansLast = drilldownPath.slice(0, indexOfMenuIdPath);
- setMenuDrilledIn(menuDrilledInSansLast);
- setDrilldownPath(pathSansLast);
+ const drillOut = (toMenuId: string, fromPathId: string, breadcrumb: JSX.Element | null) => {
+ setMenuDrilledIn(prevMenuDrilledIn => {
+ const indexOfMenuId = prevMenuDrilledIn.indexOf(toMenuId);
+ return prevMenuDrilledIn.slice(0, indexOfMenuId);
+ });
+ setDrilldownPath(prevDrilldownPath => {
+ const indexOfMenuIdPath = prevDrilldownPath.indexOf(fromPathId);
+ return prevDrilldownPath.slice(0, indexOfMenuIdPath);
+ });
setActiveMenu(toMenuId);
+ setBreadcrumb(breadcrumb);
};
const setHeight = (menuId: string, height: number) => {
@@ -146,31 +155,59 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
);
- const pauseRolloutsAppGrpBreadcrumb = (
+ const pauseRolloutsAppGrpBreadcrumb = (isOpen: boolean) => (
drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
Root
- drillOut('breadcrumbs-drilldownMenuPause', 'group:app_grouping', pauseRolloutsBreadcrumb)}
- >
- Pause rollouts
+
+ onToggle(open, 'pause-app')}>
+ 1
+
+ }
+ isOpen={isOpen}
+ dropdownItems={[
+ }
+ onClick={() => drillOut('breadcrumbs-drilldownMenuPause', 'group:app_grouping', pauseRolloutsBreadcrumb)}
+ >
+ Pause rollouts
+
+ ]}
+ />
Application Grouping
);
- const pauseRolloutsLabelsBreadcrumb = (
+ const pauseRolloutsLabelsBreadcrumb = (isOpen: boolean) => (
drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
Root
- drillOut('breadcrumbs-drilldownMenuPause', 'group:labels', pauseRolloutsBreadcrumb)}
- >
- Pause rollouts
+
+ onToggle(open, 'pause-label')}>
+ 1
+
+ }
+ isOpen={isOpen}
+ dropdownItems={[
+ }
+ onClick={() => drillOut('breadcrumbs-drilldownMenuPause', 'group:labels', pauseRolloutsBreadcrumb)}
+ >
+ Pause rollouts
+
+ ]}
+ />
Labels
@@ -205,11 +242,12 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
onDrillIn={drillIn}
onDrillOut={() => drillOut}
onGetMenuHeight={setHeight}
+ isScrollable={withMaxMenuHeight}
>
{breadcrumb && (
<>
{breadcrumb}
-
+
>
)}
@@ -270,7 +308,7 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
itemId="group:app_grouping"
description="Groups A-C"
direction="down"
- onClick={() => setBreadcrumb(pauseRolloutsAppGrpBreadcrumb)}
+ onClick={() => setBreadcrumb(pauseRolloutsAppGrpBreadcrumb(false))}
drilldownMenu={
diff --git a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx
index 2b7b0a947c6..7f59631f14c 100644
--- a/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx
+++ b/packages/react-core/src/components/MultipleFileUpload/__tests__/MultipleFileUploadStatusItem.test.tsx
@@ -99,3 +99,38 @@ describe('MultipleFileUploadStatusItem', () => {
expect(asFragment()).toMatchSnapshot();
});
});
+
+test('does not render helper text by default', () => {
+ const testFile = new File(['foo'], 'testFile.txt');
+ render(
+
+ );
+
+ const helperText = screen.queryByText('Test helper text');
+
+ expect(helperText).not.toBeInTheDocument();
+});
+
+test('renders helper text', () => {
+ const testFile = new File(['foo'], 'testFile.txt');
+ render(
+
+ );
+
+ const helperText = screen.getByText('Test helper text');
+
+ expect(helperText).toBeVisible();
+});
diff --git a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md
index c6c9474aa45..2f1c7116706 100644
--- a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md
+++ b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUpload.md
@@ -42,5 +42,11 @@ File upload - multiple is designed in a composable manner to make customization
### Basic
+The below example demonstrates a typical application of file upload - multiple, with a few tweaks from that typical application to enhance the convenience of the example.
+
+The "Show as horizontal" checkbox can be used to easily toggle the `isHorizontal` prop, showing our available styling variations.
+
+The "Demonstrate error reporting by forcing uploads to fail" checkbox shows how our `progressHelperText` prop can be used to provide status messages to users, such as when a file fails to upload. While this checkbox is checked it will cause any file uploaded to automatically fail the file reading process, and helper text will be dynamically rendered which informs the user of that error.
+
```ts file="./MultipleFileUploadBasic.tsx"
```
diff --git a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx
index aea0ad6e291..689e067a6fa 100644
--- a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx
+++ b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx
@@ -4,7 +4,9 @@ import {
MultipleFileUploadMain,
MultipleFileUploadStatus,
MultipleFileUploadStatusItem,
- Checkbox
+ Checkbox,
+ HelperText,
+ HelperTextItem
} from '@patternfly/react-core';
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';
@@ -17,6 +19,7 @@ interface readFile {
export const MultipleFileUploadBasic: React.FunctionComponent = () => {
const [isHorizontal, setIsHorizontal] = React.useState(false);
+ const [fileUploadShouldFail, setFileUploadShouldFail] = React.useState(false);
const [currentFiles, setCurrentFiles] = React.useState
([]);
const [readFileData, setReadFileData] = React.useState([]);
const [showStatus, setShowStatus] = React.useState(false);
@@ -53,6 +56,17 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
setReadFileData(newReadFiles);
};
+ /** Forces uploaded files to become corrupted if "Demonstrate error reporting by forcing uploads to fail" is selected in the example,
+ * only used in this example for demonstration purposes */
+ const updateCurrentFiles = (files: File[]) => {
+ if (fileUploadShouldFail) {
+ const corruptedFiles = files.map(file => ({ ...file, lastModified: ('foo' as unknown) as number }));
+ setCurrentFiles(prevFiles => [...prevFiles, ...corruptedFiles]);
+ } else {
+ setCurrentFiles(prevFiles => [...prevFiles, ...files]);
+ }
+ };
+
// callback that will be called by the react dropzone with the newly dropped file objects
const handleFileDrop = (droppedFiles: File[]) => {
// identify what, if any, files are re-uploads of already uploaded files
@@ -63,7 +77,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
* won't realize that the status items for the re-uploaded files needs to be re-rendered */
Promise.resolve()
.then(() => removeFiles(reUploads.map(file => file.name)))
- .then(() => setCurrentFiles(prevFiles => [...prevFiles, ...droppedFiles]));
+ .then(() => updateCurrentFiles(droppedFiles));
};
// callback called by the status item when a file is successfully read with the built-in file reader
@@ -79,6 +93,18 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
]);
};
+ // add helper text to a status item showing any error encountered during the file reading process
+ const createHelperText = (file: File) => {
+ const fileResult = readFileData.find(readFile => readFile.fileName === file.name);
+ if (fileResult?.loadError) {
+ return (
+
+ {fileResult.loadError.toString()}
+
+ );
+ }
+ };
+
const successfullyReadFileCount = readFileData.filter(fileData => fileData.loadResult === 'success').length;
return (
@@ -113,6 +139,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
onClearClick={() => removeFiles([file.name])}
onReadSuccess={handleReadSuccess}
onReadFail={handleReadFail}
+ progressHelperText={createHelperText(file)}
/>
))}
@@ -124,6 +151,12 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => {
isChecked={isHorizontal}
onChange={() => setIsHorizontal(!isHorizontal)}
/>
+ setFileUploadShouldFail(!fileUploadShouldFail)}
+ />
>
);
};
diff --git a/packages/react-core/src/components/Nav/NavItem.tsx b/packages/react-core/src/components/Nav/NavItem.tsx
index 36eae7851dd..55f50cc07d5 100644
--- a/packages/react-core/src/components/Nav/NavItem.tsx
+++ b/packages/react-core/src/components/Nav/NavItem.tsx
@@ -14,7 +14,7 @@ export interface NavItemProps extends Omit, '
styleChildren?: boolean;
/** Additional classes added to the nav item */
className?: string;
- /** Target navigation link */
+ /** Target navigation link. Should not be used if the flyout prop is defined. */
to?: string;
/** Flag indicating whether the item is active */
isActive?: boolean;
@@ -28,10 +28,12 @@ export interface NavItemProps extends Omit, '
onClick?: NavSelectClickHandler;
/** Component used to render NavItems if React.isValidElement(children) is false */
component?: React.ReactNode;
- /** Flyout of a nav item. This should be a Menu component. */
+ /** Flyout of a nav item. This should be a Menu component. Should not be used if the to prop is defined. */
flyout?: React.ReactElement;
/** Callback when flyout is opened or closed */
onShowFlyout?: () => void;
+ /** z-index of the flyout nav item */
+ zIndex?: number;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -53,6 +55,7 @@ export const NavItem: React.FunctionComponent = ({
onShowFlyout,
ouiaId,
ouiaSafe,
+ zIndex = 9999,
...props
}: NavItemProps) => {
const { flyoutRef, setFlyoutRef } = React.useContext(NavContext);
@@ -62,8 +65,14 @@ export const NavItem: React.FunctionComponent = ({
const ref = React.useRef();
const flyoutVisible = ref === flyoutRef;
const popperRef = React.useRef();
- const Component = component as any;
const hasFlyout = flyout !== undefined;
+ const Component = hasFlyout ? 'button' : (component as any);
+
+ // A NavItem should not be both a link and a flyout
+ if (to && hasFlyout) {
+ // eslint-disable-next-line no-console
+ console.error('NavItem cannot have both "to" and "flyout" props.');
+ }
const showFlyout = (show: boolean, override?: boolean) => {
if ((!flyoutVisible || override) && show) {
@@ -100,11 +109,7 @@ export const NavItem: React.FunctionComponent = ({
const key = event.key;
const target = event.target as HTMLElement;
- if (!(popperRef?.current?.contains(target) || (hasFlyout && ref?.current?.contains(target)))) {
- return;
- }
-
- if (key === ' ' || key === 'ArrowRight') {
+ if ((key === ' ' || key === 'Enter' || key === 'ArrowRight') && hasFlyout && ref?.current?.contains(target)) {
event.stopPropagation();
event.preventDefault();
if (!flyoutVisible) {
@@ -113,7 +118,9 @@ export const NavItem: React.FunctionComponent = ({
}
}
- if (key === 'Escape' || key === 'ArrowLeft') {
+ // We only want the NavItem to handle closing a flyout menu if only the first level flyout is open.
+ // Otherwise, MenuItem should handle closing its flyouts
+ if ((key === 'Escape' || key === 'ArrowLeft') && popperRef?.current?.querySelectorAll('.pf-c-menu').length === 1) {
if (flyoutVisible) {
event.stopPropagation();
event.preventDefault();
@@ -159,6 +166,8 @@ export const NavItem: React.FunctionComponent = ({
'aria-expanded': flyoutVisible
};
+ const tabIndex = isNavOpen ? null : -1;
+
const renderDefaultLink = (context: any): React.ReactNode => {
const preventLinkDefault = preventDefault || !to;
return (
@@ -172,7 +181,7 @@ export const NavItem: React.FunctionComponent = ({
className
)}
aria-current={isActive ? 'page' : null}
- tabIndex={isNavOpen ? null : '-1'}
+ tabIndex={tabIndex}
{...(hasFlyout && { ...ariaFlyoutProps })}
{...props}
>
@@ -189,7 +198,7 @@ export const NavItem: React.FunctionComponent = ({
...(styleChildren && {
className: css(styles.navLink, isActive && styles.modifiers.current, child.props && child.props.className)
}),
- tabIndex: child.props.tabIndex || isNavOpen ? null : -1,
+ tabIndex: child.props.tabIndex || tabIndex,
children: hasFlyout ? (
{child.props.children}
@@ -221,6 +230,7 @@ export const NavItem: React.FunctionComponent = ({
placement="right-start"
isVisible={flyoutVisible}
onDocumentKeyDown={handleFlyout}
+ zIndex={zIndex}
/>
);
diff --git a/packages/react-core/src/components/Nav/examples/NavFlyout.tsx b/packages/react-core/src/components/Nav/examples/NavFlyout.tsx
index 2ef83df4de6..a6a2e45e022 100644
--- a/packages/react-core/src/components/Nav/examples/NavFlyout.tsx
+++ b/packages/react-core/src/components/Nav/examples/NavFlyout.tsx
@@ -19,12 +19,7 @@ export const NavFlyout: React.FunctionComponent = () => {
);
};
diff --git a/packages/react-core/src/components/Progress/ProgressHelperText.tsx b/packages/react-core/src/components/Progress/ProgressHelperText.tsx
new file mode 100644
index 00000000000..6c2863c2f8c
--- /dev/null
+++ b/packages/react-core/src/components/Progress/ProgressHelperText.tsx
@@ -0,0 +1,20 @@
+import * as React from 'react';
+import progressStyle from '@patternfly/react-styles/css/components/Progress/progress';
+
+export interface ProgressHelperTextProps extends React.HTMLProps {
+ /** Content which can be used to convey additional information about the progress component.
+ * We recommend the helper text component as it was designed for this purpose.
+ */
+ children?: React.ReactNode;
+}
+
+export const ProgressHelperText: React.FunctionComponent = ({
+ children,
+ ...props
+}: ProgressHelperTextProps) => (
+
+ {children}
+
+);
+
+ProgressHelperText.displayName = 'ProgressHelperText';
diff --git a/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx b/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx
index 74720cd918b..1d666155b1a 100644
--- a/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx
+++ b/packages/react-core/src/components/Progress/__tests__/Progress.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { render } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import { Progress, ProgressSize } from '../Progress';
import { ProgressVariant, ProgressMeasureLocation } from '../ProgressContainer';
@@ -97,3 +97,15 @@ test('progress component generates console warning when no accessible name is pr
render( );
expect(consoleWarnMock).toHaveBeenCalled();
});
+
+test('Does not render helper text by default', () => {
+ render( );
+
+ expect(screen.queryByText('Test helper text')).not.toBeInTheDocument();
+})
+
+test('Renders passed helper text', () => {
+ render( );
+
+ expect(screen.getByText('Test helper text')).toBeVisible();
+});
diff --git a/packages/react-core/src/components/Progress/__tests__/ProgressHelperText.test.tsx b/packages/react-core/src/components/Progress/__tests__/ProgressHelperText.test.tsx
new file mode 100644
index 00000000000..24fad231a30
--- /dev/null
+++ b/packages/react-core/src/components/Progress/__tests__/ProgressHelperText.test.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { ProgressHelperText } from '../ProgressHelperText';
+
+test('Renders without children', () => {
+ render(
+
+ );
+ expect(screen.getByTestId('container').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(Test );
+
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders with class pf-c-progress__helper-text on the div containing the helper text component', () => {
+ render(Test );
+
+ expect(screen.getByText('Test')).toHaveClass('pf-c-progress__helper-text');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Progress/__tests__/__snapshots__/ProgressHelperText.test.tsx.snap b/packages/react-core/src/components/Progress/__tests__/__snapshots__/ProgressHelperText.test.tsx.snap
new file mode 100644
index 00000000000..f8e8eac47e8
--- /dev/null
+++ b/packages/react-core/src/components/Progress/__tests__/__snapshots__/ProgressHelperText.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+ test
+
+
+`;
diff --git a/packages/react-core/src/components/Progress/examples/Progress.md b/packages/react-core/src/components/Progress/examples/Progress.md
index 34ecd6c5bdd..a099e3e8698 100644
--- a/packages/react-core/src/components/Progress/examples/Progress.md
+++ b/packages/react-core/src/components/Progress/examples/Progress.md
@@ -74,3 +74,7 @@ propComponents: ['Progress']
### Title outside of progress bar
```ts file="./ProgressTitleOutsideOfProgressBar.tsx"
```
+
+### Helper text
+```ts file="./ProgressHelperText.tsx" isBeta
+```
diff --git a/packages/react-core/src/components/Progress/examples/ProgressHelperText.tsx b/packages/react-core/src/components/Progress/examples/ProgressHelperText.tsx
new file mode 100644
index 00000000000..4f3fa425937
--- /dev/null
+++ b/packages/react-core/src/components/Progress/examples/ProgressHelperText.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { Progress, ProgressProps, HelperText, HelperTextItem, Radio } from '@patternfly/react-core';
+
+export const ProgressHelperText: React.FunctionComponent = () => {
+ type ProgressVariant = ProgressProps['variant'];
+
+ const [selectedVariant, setSelectedVariant] = React.useState(undefined);
+
+ const progressVariants: ProgressVariant[] = [undefined, 'success', 'warning', 'danger'];
+
+ const capitalize = (input: string) => input[0].toUpperCase() + input.substring(1);
+ const formatVariantName = (variant: ProgressVariant) => (variant ? capitalize(variant) : 'Default');
+
+ const variantOptions = progressVariants.map(variant => (
+ setSelectedVariant(variant)}
+ key={formatVariantName(variant)}
+ name="Progress variant options"
+ />
+ ));
+
+ const helperTextVariant = selectedVariant === 'danger' ? 'error' : selectedVariant;
+
+ const helperText = (
+
+
+ {`${formatVariantName(selectedVariant)} variant is being displayed`}
+
+
+ );
+
+ return (
+ <>
+ {variantOptions}
+
+
+ >
+ );
+};
diff --git a/packages/react-core/src/components/SearchInput/AdvancedSearchMenu.tsx b/packages/react-core/src/components/SearchInput/AdvancedSearchMenu.tsx
index eeb8195f708..82055650c84 100644
--- a/packages/react-core/src/components/SearchInput/AdvancedSearchMenu.tsx
+++ b/packages/react-core/src/components/SearchInput/AdvancedSearchMenu.tsx
@@ -133,21 +133,24 @@ export const AdvancedSearchMenu: React.FunctionComponent) => {
const newMap = getAttrValueMap();
+
newMap[attribute] = newValue;
let updatedValue = '';
Object.entries(newMap).forEach(([k, v]) => {
if (v.trim() !== '') {
+ /* Wrap the value in quotes if it contains spaces */
+ const quoteWrappedValue = v.includes(' ') ? `'${v.replace(/(^'|'$)/g, '')}'` : v;
+
if (k !== 'haswords') {
- updatedValue = `${updatedValue} ${k}${advancedSearchDelimiter}${v}`;
+ updatedValue = `${updatedValue} ${k}${advancedSearchDelimiter}${quoteWrappedValue}`;
} else {
- updatedValue = `${updatedValue} ${v}`;
+ updatedValue = `${updatedValue} ${quoteWrappedValue}`;
}
}
});
- updatedValue = updatedValue.replace(/^\s+/g, '');
if (onChange) {
- onChange(updatedValue, event);
+ onChange(updatedValue.replace(/^\s+/g, ''), event);
}
};
diff --git a/packages/react-core/src/components/SearchInput/SearchInput.tsx b/packages/react-core/src/components/SearchInput/SearchInput.tsx
index 20c99dcb383..82c3701dc33 100644
--- a/packages/react-core/src/components/SearchInput/SearchInput.tsx
+++ b/packages/react-core/src/components/SearchInput/SearchInput.tsx
@@ -117,6 +117,8 @@ export interface SearchInputProps extends Omit,
placeholder?: string;
/** Accessible label for the button to navigate to previous result. */
previousNavigationButtonAriaLabel?: string;
+ /** z-index of the advanced search form when appendTo is not inline. */
+ zIndex?: number;
/** Label for the button which resets the advanced search form and clears the search input. */
resetButtonLabel?: string;
/** The number of search results returned. Either a total number of results,
@@ -157,6 +159,7 @@ const SearchInputBase: React.FunctionComponent = ({
submitSearchButtonLabel = 'Search',
isDisabled = false,
appendTo,
+ zIndex = 9999,
type = 'text',
...props
}: SearchInputProps) => {
@@ -223,13 +226,36 @@ const SearchInputBase: React.FunctionComponent = ({
setIsSearchMenuOpen(false);
};
+ const splitStringExceptInQuotes = (str: string) => {
+ let quoteType: string;
+
+ return str.match(/\\?.|^$/g).reduce(
+ (p: any, c: string) => {
+ if (c === "'" || c === '"') {
+ if (!quoteType) {
+ quoteType = c;
+ }
+ if (c === quoteType) {
+ p.quote = !p.quote;
+ }
+ } else if (!p.quote && c === ' ') {
+ p.a.push('');
+ } else {
+ p.a[p.a.length - 1] += c.replace(/\\(.)/, '$1');
+ }
+ return p;
+ },
+ { a: [''] }
+ ).a;
+ };
+
const getAttrValueMap = () => {
const attrValue: { [key: string]: string } = {};
- const pairs = searchValue.split(' ');
- pairs.map(pair => {
+ const pairs = splitStringExceptInQuotes(searchValue);
+ pairs.map((pair: string) => {
const splitPair = pair.split(advancedSearchDelimiter);
if (splitPair.length === 2) {
- attrValue[splitPair[0]] = splitPair[1];
+ attrValue[splitPair[0]] = splitPair[1].replace(/(^'|'$)/g, '');
} else if (splitPair.length === 1) {
attrValue.haswords = attrValue.hasOwnProperty('haswords')
? `${attrValue.haswords} ${splitPair[0]}`
@@ -412,6 +438,7 @@ const SearchInputBase: React.FunctionComponent = ({
isVisible={isSearchMenuOpen}
enableFlip={true}
appendTo={() => appendTo || searchInputRef.current}
+ zIndex={zIndex}
/>
);
diff --git a/packages/react-core/src/components/SearchInput/examples/SearchInput.md b/packages/react-core/src/components/SearchInput/examples/SearchInput.md
index 8dd819e7c74..665ca92271f 100644
--- a/packages/react-core/src/components/SearchInput/examples/SearchInput.md
+++ b/packages/react-core/src/components/SearchInput/examples/SearchInput.md
@@ -48,5 +48,9 @@ the following example. The search input component can also be used as a composab
or other elements to build a completely custom advanced search form. This feature is demonstrated
in the search input's react demos .
+The values used in the attribute-value form fields may contain spaces. The values containing spaces
+should be wrapped with quotes inside the summary search string in the input field. If the latter is
+autogenerated from the individual fields the quotes will be autoplaced.
+
```ts file='./SearchInputAdvanced.tsx'
```
diff --git a/packages/react-core/src/components/Select/Select.tsx b/packages/react-core/src/components/Select/Select.tsx
index ca2ce1def12..1ce9a272b73 100644
--- a/packages/react-core/src/components/Select/Select.tsx
+++ b/packages/react-core/src/components/Select/Select.tsx
@@ -183,6 +183,8 @@ export interface SelectProps
* appended inline, e.g. `menuAppendTo="parent"`
*/
isFlipEnabled?: boolean;
+ /** z-index of the select menu when menuAppendTo is not inline. */
+ zIndex?: number;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -263,7 +265,8 @@ export class Select extends React.Component
)}
diff --git a/packages/react-core/src/components/Sidebar/SidebarPanel.tsx b/packages/react-core/src/components/Sidebar/SidebarPanel.tsx
index 240066c17fe..b63feae3502 100644
--- a/packages/react-core/src/components/Sidebar/SidebarPanel.tsx
+++ b/packages/react-core/src/components/Sidebar/SidebarPanel.tsx
@@ -3,6 +3,16 @@ import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Sidebar/sidebar';
import { formatBreakpointMods } from '../../helpers/util';
+export enum SidebarPanelWidthType {
+ default = 'default',
+ width25 = 'width_25',
+ width33 = 'width_33',
+ width50 = 'width_50',
+ width66 = 'width_66',
+ width75 = 'width_75',
+ width100 = 'width_100'
+}
+
export interface SidebarPanelProps extends Omit, 'width'> {
children: React.ReactNode;
/** Indicates whether the panel is positioned statically or sticky to the top. Default is sticky on small screens when the orientation is stack, and static on medium and above screens when the orientation is split. */
diff --git a/packages/react-core/src/components/Sidebar/__tests__/Sidebar.test.tsx b/packages/react-core/src/components/Sidebar/__tests__/Sidebar.test.tsx
new file mode 100644
index 00000000000..82ac79fd0a1
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/Sidebar.test.tsx
@@ -0,0 +1,72 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { Sidebar } from '../Sidebar';
+
+test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('sidebar').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders with only class name pf-c-sidebar by default', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-c-sidebar', { exact: true });
+});
+
+test('Renders with class name pf-c-sidebar', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-c-sidebar');
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('custom-class');
+});
+
+test('Renders with class name pf-m-no-background when hasNoBackground prop is passed', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-m-no-background');
+});
+
+test('Renders with class name pf-m-stack when "stack" is passed to orientation prop', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-m-stack');
+});
+
+test('Renders with class name pf-m-split when "split" is passed to orientation prop', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-m-split');
+});
+
+test('Renders with class name pf-m-panel-right when isPanelRight prop is passed', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-m-panel-right');
+});
+
+test('Renders with class name pf-m-gutter when hasGutter prop is passed', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveClass('pf-m-gutter');
+});
+
+test('Renders with class name pf-m-sidebar__main by default for child component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-sidebar__main');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test').parentElement).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Sidebar/__tests__/SidebarContent.test.tsx b/packages/react-core/src/components/Sidebar/__tests__/SidebarContent.test.tsx
new file mode 100644
index 00000000000..8b30c2359ee
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/SidebarContent.test.tsx
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { SidebarContent } from '../SidebarContent';
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders with only class name pf-c-sidebar__content by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-sidebar__content', { exact: true });
+});
+
+test('Renders with class name pf-c-sidebar__content', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-sidebar__content');
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+});
+
+test('Renders with class name pf-m-no-background when hasNoBackground prop is passed', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-m-no-background');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Sidebar/__tests__/SidebarPanel.test.tsx b/packages/react-core/src/components/Sidebar/__tests__/SidebarPanel.test.tsx
new file mode 100644
index 00000000000..83e100c4dac
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/SidebarPanel.test.tsx
@@ -0,0 +1,75 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { SidebarPanel, SidebarPanelWidthType } from '../SidebarPanel';
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders with with only class name pf-c-sidebar__panel by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-sidebar__panel', { exact: true });
+});
+
+test('Renders with with class name pf-c-sidebar__panel', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-c-sidebar__panel');
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+});
+
+test('Renders with class name pf-m-sticky when "sticky" is passed to variant prop', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-m-sticky');
+});
+
+test('Renders with class name pf-m-static when "static" is passed to variant prop', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-m-static');
+});
+
+test('Renders with class name pf-m-no-background when hasNoBackground prop is passed', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('pf-m-no-background');
+});
+
+['width_25', 'width_33', 'width_50', 'width_66', 'width_75', 'width_100'].forEach(widthType => {
+ test(`Renders with appropriate class names when ${widthType} is passed to each breakpoint of width prop`, () => {
+ render(
+
+ Test
+
+ );
+
+ const className = widthType.replace('_', '-');
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}`);
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}-on-sm`);
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}-on-md`);
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}-on-lg`);
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}-on-xl`);
+ expect(screen.getByText('Test')).toHaveClass(`pf-m-${className}-on-2xl`);
+ });
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap
new file mode 100644
index 00000000000..0f37c4cc20a
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+
+`;
diff --git a/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarContent.test.tsx.snap b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarContent.test.tsx.snap
new file mode 100644
index 00000000000..278f53acace
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarContent.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+
+`;
diff --git a/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarPanel.test.tsx.snap b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarPanel.test.tsx.snap
new file mode 100644
index 00000000000..81236e06b34
--- /dev/null
+++ b/packages/react-core/src/components/Sidebar/__tests__/__snapshots__/SidebarPanel.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+
+`;
diff --git a/packages/react-core/src/components/Spinner/Spinner.tsx b/packages/react-core/src/components/Spinner/Spinner.tsx
index 61932f3c393..ba0ae7ae5da 100644
--- a/packages/react-core/src/components/Spinner/Spinner.tsx
+++ b/packages/react-core/src/components/Spinner/Spinner.tsx
@@ -20,6 +20,8 @@ export interface SpinnerProps extends Omit, 'si
isSVG?: boolean;
/** Diameter of spinner set as CSS variable */
diameter?: string;
+ /** @beta Indicates the spinner is inline and the size should inherit the text font size. This will override the size prop. */
+ isInline?: boolean;
/** Accessible label to describe what is loading */
'aria-label'?: string;
/** Id of element which describes what is being loaded */
@@ -33,6 +35,7 @@ export const Spinner: React.FunctionComponent = ({
'aria-valuetext': ariaValueText = 'Loading...',
isSVG = true,
diameter,
+ isInline = false,
'aria-label': ariaLabel,
'aria-labelledBy': ariaLabelledBy,
...props
@@ -41,7 +44,7 @@ export const Spinner: React.FunctionComponent = ({
return (
(
+
+
+
+ Heading
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit Sed hendrerit nisi in cursus maximus.
+
+
+ Second level
+
+
+
+ Curabitur accumsan turpis pharetra blandit. Quisque condimentum maximus mi,{' '}
+ sit amet commodo arcu rutrum id. Proin pretium
+ urna vel cursus venenatis. Suspendisse potenti.
+
+
+ Sometimes you need small text
+
+
+
+
+);
diff --git a/packages/react-core/src/components/Tabs/OverflowTab.tsx b/packages/react-core/src/components/Tabs/OverflowTab.tsx
index 0d715ff4feb..0edf1387133 100644
--- a/packages/react-core/src/components/Tabs/OverflowTab.tsx
+++ b/packages/react-core/src/components/Tabs/OverflowTab.tsx
@@ -19,6 +19,8 @@ export interface OverflowTabProps extends React.HTMLProps {
defaultTitleText?: string;
/** The aria label applied to the button which toggles the tab overflow menu */
toggleAriaLabel?: string;
+ /** z-index of the overflow tab */
+ zIndex?: number;
}
export const OverflowTab: React.FunctionComponent = ({
@@ -27,6 +29,7 @@ export const OverflowTab: React.FunctionComponent = ({
showTabCount,
defaultTitleText = 'More',
toggleAriaLabel,
+ zIndex = 9999,
...props
}: OverflowTabProps) => {
const menuRef = React.useRef();
@@ -138,6 +141,7 @@ export const OverflowTab: React.FunctionComponent = ({
isVisible={isExpanded}
popperMatchesTriggerWidth={false}
appendTo={overflowLIRef.current}
+ zIndex={zIndex}
/>
);
diff --git a/packages/react-core/src/components/Tabs/Tab.tsx b/packages/react-core/src/components/Tabs/Tab.tsx
index 12acef004ca..5867704a163 100644
--- a/packages/react-core/src/components/Tabs/Tab.tsx
+++ b/packages/react-core/src/components/Tabs/Tab.tsx
@@ -5,10 +5,12 @@ import { TabButton } from './TabButton';
import { TabsContext } from './TabsContext';
import { css } from '@patternfly/react-styles';
import { Tooltip } from '../Tooltip';
-import { Button } from '../Button';
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
+import { TabAction } from './TabAction';
-export interface TabProps extends Omit, 'title'>, OUIAProps {
+export interface TabProps
+ extends Omit, 'title' | 'action'>,
+ OUIAProps {
/** content rendered inside the Tab content area. */
children?: React.ReactNode;
/** additional classes added to the Tab */
@@ -39,6 +41,8 @@ export interface TabProps extends Omit = ({
tooltip,
closeButtonAriaLabel,
isCloseDisabled = false,
+ actions,
...props
}: TabProps) => {
const preventedEvents = inoperableEvents.reduce(
@@ -117,26 +122,22 @@ const TabBase: React.FunctionComponent = ({
className={css(
styles.tabsItem,
eventKey === localActiveKey && styles.modifiers.current,
- handleTabClose && styles.modifiers.action,
- handleTabClose && (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
+ (handleTabClose || actions) && styles.modifiers.action,
+ (isDisabled || isAriaDisabled) && styles.modifiers.disabled,
childClassName
)}
role="presentation"
>
{tooltip ? {tabButton} : tabButton}
+ {actions && actions}
{handleTabClose !== undefined && (
-
- handleTabClose(event, eventKey, tabContentRef)}
- isDisabled={isCloseDisabled}
- >
-
-
-
-
-
+ handleTabClose(event, eventKey, tabContentRef)}
+ isDisabled={isCloseDisabled}
+ >
+
+
)}
);
diff --git a/packages/react-core/src/components/Tabs/TabAction.tsx b/packages/react-core/src/components/Tabs/TabAction.tsx
new file mode 100644
index 00000000000..04bc4e597ee
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/TabAction.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { css } from '@patternfly/react-styles';
+import styles from '@patternfly/react-styles/css/components/Tabs/tabs';
+import { Button } from '../Button';
+import { getOUIAProps, OUIAProps } from '../../helpers';
+
+export interface TabActionProps extends Omit, 'ref' | 'type' | 'size'>, OUIAProps {
+ /** Content rendered in the tab action */
+ children?: React.ReactNode;
+ /** Additional classes added to the tab action span */
+ className?: string;
+ /** Click callback for tab action button */
+ onClick?: (event: React.MouseEvent) => void;
+ /** Flag indicating if the tab action is a help action */
+ isHelpAction?: boolean;
+ /** Flag indicating if the tab action is disabled */
+ isDisabled?: boolean;
+ /** Accessible label for the tab action */
+ 'aria-label'?: string;
+ /** @hide Callback for the section ref */
+ innerRef?: React.Ref;
+}
+
+const TabActionBase: React.FunctionComponent = ({
+ children,
+ className,
+ onClick,
+ isHelpAction,
+ isDisabled,
+ 'aria-label': ariaLabel = 'Tab action',
+ innerRef,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ouiaId,
+ ouiaSafe,
+ ...props
+}: TabActionProps) => (
+
+
+ {children}
+
+
+);
+
+export const TabAction = React.forwardRef((props: TabActionProps, ref: React.Ref) => (
+
+));
+
+TabAction.displayName = 'TabAction';
diff --git a/packages/react-core/src/components/Tabs/Tabs.tsx b/packages/react-core/src/components/Tabs/Tabs.tsx
index caf128c6b3e..00a175dbc28 100644
--- a/packages/react-core/src/components/Tabs/Tabs.tsx
+++ b/packages/react-core/src/components/Tabs/Tabs.tsx
@@ -45,7 +45,7 @@ export interface TabsProps extends Omit, eventKey: number | string) => void;
- /** @beta Callback to handle tab closing */
+ /** @beta Callback to handle tab closing and adds a basic close button to all tabs. This is overridden by the tab actions property. */
onClose?: (event: React.MouseEvent, eventKey: number | string) => void;
/** @beta Callback for the add button. Passing this property inserts the add button */
onAdd?: () => void;
diff --git a/packages/react-core/src/components/Tabs/__tests__/Tab.test.tsx b/packages/react-core/src/components/Tabs/__tests__/Tab.test.tsx
index 2f3d9c79452..677811072be 100644
--- a/packages/react-core/src/components/Tabs/__tests__/Tab.test.tsx
+++ b/packages/react-core/src/components/Tabs/__tests__/Tab.test.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { Tab } from '../Tab';
+import { TabAction } from '../TabAction';
import { TabTitleText } from '../TabTitleText';
test('should not render anything', () => {
@@ -11,3 +12,12 @@ test('should not render anything', () => {
);
expect(asFragment()).toMatchSnapshot();
});
+
+test('renders tab action', () => {
+ const { asFragment } = render(
+ "Tab item 2"} actions={test }>
+ Tab 2 section
+
+ );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap b/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap
index 32cd9db47f4..74166f26005 100644
--- a/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap
+++ b/packages/react-core/src/components/Tabs/__tests__/__snapshots__/Tab.test.tsx.snap
@@ -1,5 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`renders tab action 1`] = `
+
+
+
+
+ "Tab item 2"
+
+
+
+
+
+ test
+
+
+
+
+
+`;
+
exports[`should not render anything 1`] = `
{
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- const tooltip = (
-
- );
-
- return (
-
-
- Users} aria-label="Default content - users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Disabled} isDisabled>
- Disabled
-
- ARIA Disabled} isAriaDisabled>
- ARIA Disabled
-
- ARIA Disabled (Tooltip)}
- isAriaDisabled
- >
- ARIA Disabled (Tooltip)
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsDefault.tsx"
```
### With tooltip react ref
@@ -109,305 +34,22 @@ When using a React ref to link a Tooltip to a Tab component via the `reference`
The tooltip should also have the `id` prop passed in. The value given to this prop should then be passed into the tab's `aria-describedby` prop. This ensures a tooltip used with a React ref will be announced by the JAWS and NVDA screen readers.
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
-
-class SimpleTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- const tooltipRef = React.createRef();
-
- return (
-
-
- Users} aria-label="Tooltip ref content - users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Disabled} isDisabled>
- Disabled
-
- ARIA Disabled} isAriaDisabled>
- ARIA Disabled
-
- ARIA Disabled (Tooltip)}
- isAriaDisabled
- ref={tooltipRef}
- aria-describedby="tooltip-ref1"
- >
- ARIA Disabled (Tooltip)
-
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsTooltipReactRef.tsx"
```
### Uncontrolled
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
-
-class UncontrolledSimpleTabs extends React.Component {
- render() {
- const tooltip = (
-
- );
-
- return (
- <>
-
- Users} aria-label="Uncontrolled ref content - users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Disabled} isDisabled>
- Disabled
-
- ARIA Disabled} isAriaDisabled>
- ARIA Disabled
-
- ARIA Disabled (Tooltip)}
- isAriaDisabled
- tooltip={tooltip}
- >
- ARIA Disabled (Tooltip)
-
-
- >
- );
- }
-}
+```ts file="./TabsUncontrolled.tsx"
```
### Box light variation
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
-
-class SimpleTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isTabsLightScheme: true
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleScheme = checked => {
- this.setState({
- isTabsLightScheme: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox, isTabsLightScheme } = this.state;
- const tooltip = (
-
- );
-
- return (
-
-
- Users} aria-label="Box light variation content - users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Disabled} isDisabled>
- Disabled
-
- ARIA Disabled} isAriaDisabled>
- ARIA Disabled
-
- ARIA Disabled (Tooltip)}
- isAriaDisabled
- tooltip={tooltip}
- >
- ARIA Disabled (Tooltip)
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsBoxLight.tsx"
```
### Default overflow
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-
-class ScrollButtonsPrimaryTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
-
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- return (
-
-
- Users} aria-label="Default overflow content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
- Tab item 7}>
- Tab 7 section
-
- Tab item 8}>
- Tab 8 section
-
- Tab item 9}>
- Tab 9 section
-
- Tab item 10}>
- Tab 10 section
-
- Tab item 11}>
- Tab 11 section
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsDefaultOverflow.tsx"
```
### Horizontal overflow
@@ -417,1226 +59,82 @@ class ScrollButtonsPrimaryTabs extends React.Component {
### Vertical
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
-
-class VerticalTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- const tooltip = (
-
- );
-
- return (
-
-
- Users} aria-label="Vertical example content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Disabled} isDisabled>
- Disabled
-
- ARIA Disabled} isAriaDisabled>
- ARIA Disabled
-
- ARIA Disabled (Tooltip)}
- isAriaDisabled
- tooltip={tooltip}
- >
- ARIA Disabled (Tooltip)
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsVertical.tsx"
```
### Vertical expandable
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-class VerticalExpandableTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isExpanded: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- this.onToggle = isExpanded => {
- this.setState({
- isExpanded
- });
- };
- }
- render() {
- const { activeTabKey, isExpanded } = this.state;
- return (
-
- Users} aria-label="Vertical expandable content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
-
- );
- }
-}
+```ts file="./TabsVerticalExpandable.tsx"
```
### Vertical expandable uncontrolled
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-class VerticalExpandableUncontrolledTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
- render() {
- const { activeTabKey } = this.state;
- return (
-
- Users} aria-label="Vertical expandable uncontrolled content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
-
- );
- }
-}
+```ts file="./TabsVerticalExpandableUncontrolled.tsx"
```
### Inset
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-
-class InsetTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- return (
-
-
- Users} aria-label="Inset example content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsInset.tsx"
```
### Page Insets
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-
-class PageInsetsTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- return (
-
-
- Users} aria-label="Page insets example content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsPageInsets.tsx"
```
### Icons and text
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, TabTitleIcon } from '@patternfly/react-core';
-import UsersIcon from '@patternfly/react-icons/dist/esm/icons/users-icon';
-import BoxIcon from '@patternfly/react-icons/dist/esm/icons/box-icon';
-import DatabaseIcon from '@patternfly/react-icons/dist/esm/icons/database-icon';
-import ServerIcon from '@patternfly/react-icons/dist/esm/icons/server-icon';
-import LaptopIcon from '@patternfly/react-icons/dist/esm/icons/laptop-icon';
-import ProjectDiagramIcon from '@patternfly/react-icons/dist/esm/icons/project-diagram-icon';
-
-class IconAndTextTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- return (
-
-
-
-
- {' '}
- Users {' '}
- >
- }
- aria-label="icons and text content"
- >
- Users
-
-
-
-
- {' '}
- Containers {' '}
- >
- }
- >
- Containers
-
-
-
-
- {' '}
- Database {' '}
- >
- }
- >
- Database
-
-
-
-
- {' '}
- Server {' '}
- >
- }
- >
- Server
-
-
-
-
- {' '}
- System {' '}
- >
- }
- >
- System
-
-
-
-
- {' '}
- Network {' '}
- >
- }
- >
- Network
-
-
- );
- }
-}
+```ts file="./TabsIconAndText.tsx"
```
### Tabs with sub tabs
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-
-class SecondaryTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey1: 0,
- activeTabKey2: 10,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClickFirst = (event, tabIndex) => {
- this.setState({
- activeTabKey1: tabIndex
- });
- };
- // Toggle currently active secondary tab
- this.handleTabClickSecond = (event, tabIndex) => {
- this.setState({
- activeTabKey2: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey1, activeTabKey2, isBox } = this.state;
- return (
-
-
- Users} aria-label="Tabs with subtabs content users">
-
- Secondary tab item 1}>
- Secondary tab item 1 item section
-
- Secondary tab item 2}>
- Secondary tab item 2 section
-
- Secondary tab item 3}>
- Secondary tab item 3 section
-
- Secondary tab item 4}>
- Secondary tab item 4 section
-
- Secondary tab item 5}>
- Secondary tab item 5 section
-
- Secondary tab item 6}>
- Secondary tab item 6 section
-
- Secondary tab item 7}>
- Secondary tab item 7 section
-
- Secondary tab item 8}>
- Secondary tab item 8 section
-
-
-
- Containers}>
- Containers
-
- Database}>
- Database
-
- Server}>
- Server
-
- System}>
- System
-
- Network}>
- Network
-
- Tab item 7}>
- Tab 7 section
-
- Tab item 8}>
- Tab 8 section
-
- Tab item 9}>
- Tab 9 section
-
- Tab item 10}>
- Tab 10 section
-
- Tab item 11}>
- Tab 11 section
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsSubtabs.tsx"
```
### Filled
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
-
-class FilledTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- return (
-
-
- Users} aria-label="Tabs filled example content users">
- Users
-
- Containers}>
- Containers
-
- Database}>
- Database
-
-
-
-
-
-
- );
- }
-}
+```ts file="TabsFilled.tsx"
```
### Filled with icons
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, TabTitleIcon, Checkbox } from '@patternfly/react-core';
-import UsersIcon from '@patternfly/react-icons/dist/esm/icons/users-icon';
-import BoxIcon from '@patternfly/react-icons/dist/esm/icons/box-icon';
-import DatabaseIcon from '@patternfly/react-icons/dist/esm/icons/database-icon';
-
-class FilledTabsWithIcons extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0,
- isBox: false
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
-
- this.toggleBox = checked => {
- this.setState({
- isBox: checked
- });
- };
- }
-
- render() {
- const { activeTabKey, isBox } = this.state;
- return (
-
-
-
-
-
- {' '}
- Users {' '}
- >
- }
- aria-label="filled tabs with icons content users"
- >
- Users
-
-
-
-
- {' '}
- Containers {' '}
- >
- }
- >
- Containers
-
-
-
-
- {' '}
- Database {' '}
- >
- }
- >
- Database
-
-
-
-
-
-
- );
- }
-}
+```ts file="./TabsFilledWithIcons.tsx"
```
### Using the nav element
-```js
-import React from 'react';
-import { Tabs, Tab, TabsComponent, TabTitleText } from '@patternfly/react-core';
-
-class TabsNav extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0
- };
-
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- return (
-
- Users} href="#users" aria-label="Nav element content users">
- Users
-
- Containers} href="#containers">
- Containers
-
- Database} href="#database">
- Database
-
- Disabled} isDisabled href="#disabled">
- Disabled
-
- ARIA Disabled} isAriaDisabled href="#aria-disabled">
- ARIA Disabled
-
- Network} href="#network">
- Network
-
-
- );
- }
-}
+```ts file="./TabsNav.tsx"
```
### Sub nav using the nav element
-```js
-import React from 'react';
-import { Tabs, Tab, TabsComponent, TabTitleText } from '@patternfly/react-core';
-
-class SecondaryTabsNav extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey1: 0,
- activeTabKey2: 10
- };
-
- // Toggle currently active tab
- this.handleTabClickFirst = (event, tabIndex) => {
- this.setState({
- activeTabKey1: tabIndex
- });
- };
-
- // Toggle currently active secondary tab
- this.handleTabClickSecond = (event, tabIndex) => {
- this.setState({
- activeTabKey2: tabIndex
- });
- };
- }
-
- render() {
- return (
-
- Users} href="#" aria-label="Subtabs with nav content users">
-
- Item 1} href="#">
- Item 1 item section
-
- Item 2} href="#">
- Item 2 section
-
- Item 3} href="#">
- Item 3 section
-
- Disabled} isDisabled href="#">
- Disabled
-
- ARIA Disabled} isAriaDisabled href="#">
- ARIA Disabled
-
- Item 6} href="#">
- Item 6 section
-
-
-
- Containers} href="#">
- Containers
-
- Database} href="#">
- Database
-
- Disabled} isDisabled href="#">
- Disabled
-
- ARIA Disabled} isAriaDisabled href="#">
- ARIA Disabled
-
- Network} href="#">
- Network
-
-
- );
- }
-}
+```ts file="./TabsNavSecondary.tsx"
```
### Separate content
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, TabContent } from '@patternfly/react-core';
-
-class SeparateTabContent extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeKey: 0
- };
-
- this.contentRef1 = React.createRef();
- this.contentRef2 = React.createRef();
- this.contentRef3 = React.createRef();
-
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- return (
-
-
- Tab item 1}
- tabContentId="refTab1Section"
- tabContentRef={this.contentRef1}
- />
- Tab item 2}
- tabContentId="refTab2Section"
- tabContentRef={this.contentRef2}
- />
- Tab item 3}
- tabContentId="refTab3Section"
- tabContentRef={this.contentRef3}
- />
-
-
-
- Tab 1 section
-
-
- Tab 2 section
-
-
- Tab 3 section
-
-
-
- );
- }
-}
+```ts file="./TabsSeparateContent.tsx"
```
### Tab content with body and padding
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText, TabContent, TabContentBody } from '@patternfly/react-core';
-
-const TabContentWithBody = () => {
- const [activeTabKey, setActiveTabKey] = React.useState(0);
-
- // Toggle currently active tab
- const handleTabClick = (event, tabIndex) => {
- setActiveTabKey(tabIndex);
- };
-
- const contentRef1 = React.createRef();
- const contentRef2 = React.createRef();
- const contentRef3 = React.createRef();
-
- return (
-
-
- Tab item 1}
- tabContentId="tab1SectionBodyPadding"
- tabContentRef={contentRef1}
- />
- Tab item 2}
- tabContentId="tab2SectionBodyPadding"
- tabContentRef={contentRef2}
- />
- Tab item 3}
- tabContentId="tab3SectionBodyPadding"
- tabContentRef={contentRef3}
- />
-
-
-
- Tab 1 section with body and padding
-
-
- Tab 2 section with body and padding
-
-
- Tab 3 section with body and padding
-
-
-
- );
-};
+```ts file="./TabsContentWithBodyPadding.tsx"
```
### Children mounting on click
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
-
-class MountingSimpleTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- return (
-
- Tab item 1} aria-label="Tab 1">
- Tab 1 section
-
- Tab item 2} aria-label="Tab 2">
- Tab 2 section
-
- Tab item 3} aria-label="Tab 3">
- Tab 3 section
-
-
- );
- }
-}
+```ts file="./TabsChildrenMounting.tsx"
```
### Unmounting invisible children
-```js
-import React from 'react';
-import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
-
-class UnmountingSimpleTabs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeTabKey: 0
- };
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- return (
-
- Tab item 1} aria-label="Invisible children tab 1">
- Tab 1 section
-
- Tab item 2} aria-label="Invisible children tab 2">
- Tab 2 section
-
- Tab item 3} aria-label="Invisible children tab 3">
- Tab 3 section
-
-
- );
- }
-}
+```ts file="./TabsUnmountingInvisibleChildren.tsx"
```
### Toggled separate content
-```js
-import React from 'react';
-import { Tabs, Tab, TabContent, Button, Divider } from '@patternfly/react-core';
-
-class ToggledSeparateContent extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeKey: 0,
- isTab2Hidden: false
- };
-
- this.contentRef1 = React.createRef();
- this.contentRef2 = React.createRef();
- this.contentRef3 = React.createRef();
-
- // Toggle currently active tab
- this.handleTabClick = (event, tabIndex) => {
- this.setState({
- activeTabKey: tabIndex
- });
- };
- }
-
- render() {
- const { activeKey, isTab2Hidden } = this.state;
- return (
-
- this.setState({ isTab2Hidden: !isTab2Hidden })}>
- {isTab2Hidden ? 'Show' : 'Hide'} tab 2
-
-
-
-
- {!isTab2Hidden && (
-
- )}
-
-
-
-
- Tab 1 section
-
- {!isTab2Hidden && (
-
- Tab 2 section
-
- )}
-
- Tab 3 section
-
-
-
- );
- }
-}
+```ts file="./TabsToggledSeparateContent.tsx"
```
### Dynamic
@@ -1645,3 +143,13 @@ To enable closeable tabs, pass the `onClose` property to `Tabs`, and to enable t
```ts file="./TabsDynamic.tsx" isBeta
```
+
+### Help action
+
+```ts file="./TabsHelp.tsx" isBeta
+```
+
+### Help and close actions
+
+```ts file="./TabsHelpAndClose.tsx" isBeta
+```
diff --git a/packages/react-core/src/components/Tabs/examples/TabsBoxLight.tsx b/packages/react-core/src/components/Tabs/examples/TabsBoxLight.tsx
new file mode 100644
index 00000000000..58b1d392e35
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsBoxLight.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
+
+export const TabsBoxLight: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isTabsLightScheme, setIsTabsLightScheme] = React.useState(true);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleScheme = (checked: boolean) => {
+ setIsTabsLightScheme(checked);
+ };
+
+ const tooltip = (
+
+ );
+ return (
+
+
+ Users} aria-label="Box light variation content - users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Disabled} isDisabled>
+ Disabled
+
+ ARIA Disabled} isAriaDisabled>
+ ARIA Disabled
+
+ ARIA Disabled (Tooltip)} isAriaDisabled tooltip={tooltip}>
+ ARIA Disabled (Tooltip)
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsChildrenMounting.tsx b/packages/react-core/src/components/Tabs/examples/TabsChildrenMounting.tsx
new file mode 100644
index 00000000000..23e189e63ca
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsChildrenMounting.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
+
+export const TabsChildrenMounting: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ return (
+
+ Tab item 1} aria-label="Tab 1">
+ Tab 1 section
+
+ Tab item 2} aria-label="Tab 2">
+ Tab 2 section
+
+ Tab item 3} aria-label="Tab 3">
+ Tab 3 section
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsContentWithBodyPadding.tsx b/packages/react-core/src/components/Tabs/examples/TabsContentWithBodyPadding.tsx
new file mode 100644
index 00000000000..1271559f18c
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsContentWithBodyPadding.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, TabContent, TabContentBody } from '@patternfly/react-core';
+
+export const TabContentWithBodyPadding: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const contentRef1 = React.createRef();
+ const contentRef2 = React.createRef();
+ const contentRef3 = React.createRef();
+
+ return (
+
+
+ Tab item 1}
+ tabContentId="tab1SectionBodyPadding"
+ tabContentRef={contentRef1}
+ />
+ Tab item 2}
+ tabContentId="tab2SectionBodyPadding"
+ tabContentRef={contentRef2}
+ />
+ Tab item 3}
+ tabContentId="tab3SectionBodyPadding"
+ tabContentRef={contentRef3}
+ />
+
+
+
+ Tab 1 section with body and padding
+
+
+ Tab 2 section with body and padding
+
+
+ Tab 3 section with body and padding
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsDefault.tsx b/packages/react-core/src/components/Tabs/examples/TabsDefault.tsx
new file mode 100644
index 00000000000..dadc08edb48
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsDefault.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
+
+export const TabsDefault: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ const tooltip = (
+
+ );
+ return (
+
+
+ Users} aria-label="Default content - users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Disabled} isDisabled>
+ Disabled
+
+ ARIA Disabled} isAriaDisabled>
+ ARIA Disabled
+
+ ARIA Disabled (Tooltip)} isAriaDisabled>
+ ARIA Disabled (Tooltip)
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsDefaultOverflow.tsx b/packages/react-core/src/components/Tabs/examples/TabsDefaultOverflow.tsx
new file mode 100644
index 00000000000..caa78a3791f
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsDefaultOverflow.tsx
@@ -0,0 +1,74 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
+
+export const TabsDefaultOverflow: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+ Users} aria-label="Default overflow content users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+ Tab item 7}>
+ Tab 7 section
+
+ Tab item 8}>
+ Tab 8 section
+
+ Tab item 9}>
+ Tab 9 section
+
+ Tab item 10}>
+ Tab 10 section
+
+ Tab item 11}>
+ Tab 11 section
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsFilled.tsx b/packages/react-core/src/components/Tabs/examples/TabsFilled.tsx
new file mode 100644
index 00000000000..6802cf36d25
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsFilled.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
+
+export const TabsFilled: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+ Users} aria-label="Tabs filled example content users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsFilledWithIcons.tsx b/packages/react-core/src/components/Tabs/examples/TabsFilledWithIcons.tsx
new file mode 100644
index 00000000000..613434fa22b
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsFilledWithIcons.tsx
@@ -0,0 +1,85 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, TabTitleIcon, Checkbox } from '@patternfly/react-core';
+import UsersIcon from '@patternfly/react-icons/dist/esm/icons/users-icon';
+import BoxIcon from '@patternfly/react-icons/dist/esm/icons/box-icon';
+import DatabaseIcon from '@patternfly/react-icons/dist/esm/icons/database-icon';
+
+export const TabsFilledWithIcons: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+
+
+
+ {' '}
+ Users {' '}
+ >
+ }
+ aria-label="filled tabs with icons content users"
+ >
+ Users
+
+
+
+
+ {' '}
+ Containers {' '}
+ >
+ }
+ >
+ Containers
+
+
+
+
+ {' '}
+ Database {' '}
+ >
+ }
+ >
+ Database
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsHelp.tsx b/packages/react-core/src/components/Tabs/examples/TabsHelp.tsx
new file mode 100644
index 00000000000..32e287546da
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsHelp.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, TabAction, Popover } from '@patternfly/react-core';
+import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
+
+export const TabsHelp: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+
+ const tabs = ['Users', 'Containers', 'Database', 'Disabled', 'ARIA disabled', 'Help disabled'];
+
+ const helpPopover = (header: string, popoverRef: React.RefObject) => (
+ {header} }
+ bodyContent={
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ footerContent="Popover footer"
+ reference={popoverRef}
+ />
+ );
+
+ return (
+ setActiveTabKey(tabIndex as number)}
+ aria-label="Tabs in the help action example"
+ role="region"
+ >
+ {tabs.map((tab, index) => {
+ const ref = React.createRef();
+
+ return (
+ {tab}}
+ {...(tab === 'Disabled' && { isDisabled: true })}
+ {...(tab === 'ARIA disabled' && { isAriaDisabled: true })}
+ actions={
+ <>
+
+
+
+ {helpPopover(`Help popover for ${tab}`, ref)}
+ >
+ }
+ >
+ {tab}
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsHelpAndClose.tsx b/packages/react-core/src/components/Tabs/examples/TabsHelpAndClose.tsx
new file mode 100644
index 00000000000..2c409a5fbae
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsHelpAndClose.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Popover, TabAction } from '@patternfly/react-core';
+import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
+import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
+
+export const TabsHelpAndClose: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [tabs, setTabs] = React.useState(['Terminal 1', 'Terminal 2', 'Terminal 3']);
+ const tabComponentRef = React.useRef();
+ const firstMount = React.useRef(true);
+
+ const onClose = (event: any, tabIndex: string | number) => {
+ const tabIndexNum = tabIndex as number;
+ let nextTabIndex = activeTabKey;
+ if (tabIndexNum < activeTabKey) {
+ // if a preceding tab is closing, keep focus on the new index of the current tab
+ nextTabIndex = activeTabKey - 1 > 0 ? activeTabKey - 1 : 0;
+ } else if (activeTabKey === tabs.length - 1) {
+ // if the closing tab is the last tab, focus the preceding tab
+ nextTabIndex = tabs.length - 2 > 0 ? tabs.length - 2 : 0;
+ }
+ setActiveTabKey(nextTabIndex);
+ setTabs(tabs.filter((tab, index) => index !== tabIndex));
+ };
+
+ const helpPopover = (header: string, popoverRef: React.RefObject) => (
+ {header} }
+ bodyContent={
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ footerContent="Popover footer"
+ reference={popoverRef}
+ />
+ );
+
+ React.useEffect(() => {
+ if (firstMount.current) {
+ firstMount.current = false;
+ return;
+ } else {
+ const first = tabComponentRef.current.tabList.current.childNodes[activeTabKey];
+ first && first.firstChild.focus();
+ }
+ }, [tabs]);
+
+ return (
+ setActiveTabKey(tabIndex as number)}
+ aria-label="Tabs in the help enabled and closeable example"
+ role="region"
+ ref={tabComponentRef}
+ >
+ {tabs.map((tab, index) => {
+ const ref = React.createRef();
+
+ return (
+ {tab}}
+ actions={
+ <>
+
+
+
+ onClose(e, index)} isDisabled={tabs.length === 1}>
+
+
+ {helpPopover(`Help popover for ${tab}`, ref)}
+ >
+ }
+ >
+ {tab}
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsIconAndText.tsx b/packages/react-core/src/components/Tabs/examples/TabsIconAndText.tsx
new file mode 100644
index 00000000000..6c0da805c7f
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsIconAndText.tsx
@@ -0,0 +1,108 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, TabTitleIcon } from '@patternfly/react-core';
+import UsersIcon from '@patternfly/react-icons/dist/esm/icons/users-icon';
+import BoxIcon from '@patternfly/react-icons/dist/esm/icons/box-icon';
+import DatabaseIcon from '@patternfly/react-icons/dist/esm/icons/database-icon';
+import ServerIcon from '@patternfly/react-icons/dist/esm/icons/server-icon';
+import LaptopIcon from '@patternfly/react-icons/dist/esm/icons/laptop-icon';
+import ProjectDiagramIcon from '@patternfly/react-icons/dist/esm/icons/project-diagram-icon';
+
+export const TabsIconAndText: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ return (
+
+
+
+
+ {' '}
+ Users {' '}
+ >
+ }
+ aria-label="icons and text content"
+ >
+ Users
+
+
+
+
+ {' '}
+ Containers {' '}
+ >
+ }
+ >
+ Containers
+
+
+
+
+ {' '}
+ Database {' '}
+ >
+ }
+ >
+ Database
+
+
+
+
+ {' '}
+ Server {' '}
+ >
+ }
+ >
+ Server
+
+
+
+
+ {' '}
+ System {' '}
+ >
+ }
+ >
+ System
+
+
+
+
+ {' '}
+ Network {' '}
+ >
+ }
+ >
+ Network
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsInset.tsx b/packages/react-core/src/components/Tabs/examples/TabsInset.tsx
new file mode 100644
index 00000000000..6f9964d6627
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsInset.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
+
+export const TabsInset: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+ Users} aria-label="Inset example content users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsNav.tsx b/packages/react-core/src/components/Tabs/examples/TabsNav.tsx
new file mode 100644
index 00000000000..42494ea94f0
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsNav.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Tabs, Tab, TabsComponent, TabTitleText } from '@patternfly/react-core';
+
+export const TabsNav: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+ return (
+
+ Users} href="#users" aria-label="Nav element content users">
+ Users
+
+ Containers} href="#containers">
+ Containers
+
+ Database} href="#database">
+ Database
+
+ Disabled} isDisabled href="#disabled">
+ Disabled
+
+ ARIA Disabled} isAriaDisabled href="#aria-disabled">
+ ARIA Disabled
+
+ Network} href="#network">
+ Network
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsNavSecondary.tsx b/packages/react-core/src/components/Tabs/examples/TabsNavSecondary.tsx
new file mode 100644
index 00000000000..7102c5aca73
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsNavSecondary.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { Tabs, Tab, TabsComponent, TabTitleText } from '@patternfly/react-core';
+
+export const TabsNavSecondary: React.FunctionComponent = () => {
+ const [activeTabKey1, setActiveTabKey1] = React.useState(0);
+ const [activeTabKey2, setActiveTabKey2] = React.useState(0);
+
+ // Toggle currently active primary tab
+ const handleTabClickFirst = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey1(tabIndex);
+ };
+
+ // Toggle currently active secondary tab
+ const handleTabClickSecond = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey2(tabIndex);
+ };
+
+ return (
+
+ Users} href="#" aria-label="Subtabs with nav content users">
+
+ Item 1} href="#">
+ Item 1 item section
+
+ Item 2} href="#">
+ Item 2 section
+
+ Item 3} href="#">
+ Item 3 section
+
+ Disabled} isDisabled href="#">
+ Disabled
+
+ ARIA Disabled} isAriaDisabled href="#">
+ ARIA Disabled
+
+ Item 6} href="#">
+ Item 6 section
+
+
+
+ Containers} href="#">
+ Containers
+
+ Database} href="#">
+ Database
+
+ Disabled} isDisabled href="#">
+ Disabled
+
+ ARIA Disabled} isAriaDisabled href="#">
+ ARIA Disabled
+
+ Network} href="#">
+ Network
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsPageInsets.tsx b/packages/react-core/src/components/Tabs/examples/TabsPageInsets.tsx
new file mode 100644
index 00000000000..e0a5429a396
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsPageInsets.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
+
+export const TabsPageInsets: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+ Users} aria-label="Page insets example content users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsSeparateContent.tsx b/packages/react-core/src/components/Tabs/examples/TabsSeparateContent.tsx
new file mode 100644
index 00000000000..a07c3455636
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsSeparateContent.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, TabContent } from '@patternfly/react-core';
+
+export const TabsSeparateContent: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const contentRef1 = React.createRef();
+ const contentRef2 = React.createRef();
+ const contentRef3 = React.createRef();
+
+ return (
+
+
+ Tab item 1}
+ tabContentId="refTab1Section"
+ tabContentRef={contentRef1}
+ />
+ Tab item 2}
+ tabContentId="refTab2Section"
+ tabContentRef={contentRef2}
+ />
+ Tab item 3}
+ tabContentId="refTab3Section"
+ tabContentRef={contentRef3}
+ />
+
+
+
+ Tab 1 section
+
+
+ Tab 2 section
+
+
+ Tab 3 section
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsSubtabs.tsx b/packages/react-core/src/components/Tabs/examples/TabsSubtabs.tsx
new file mode 100644
index 00000000000..2994a622641
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsSubtabs.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox } from '@patternfly/react-core';
+
+export const TabsSubtabs: React.FunctionComponent = () => {
+ const [activeTabKey1, setActiveTabKey1] = React.useState(0);
+ const [activeTabKey2, setActiveTabKey2] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+
+ // Toggle currently active primary tab
+ const handleTabClickFirst = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey1(tabIndex);
+ };
+
+ // Toggle currently active secondary tab
+ const handleTabClickSecond = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey2(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ return (
+
+
+ Users} aria-label="Tabs with subtabs content users">
+
+ Secondary tab item 1}>
+ Secondary tab item 1 item section
+
+ Secondary tab item 2}>
+ Secondary tab item 2 section
+
+ Secondary tab item 3}>
+ Secondary tab item 3 section
+
+ Secondary tab item 4}>
+ Secondary tab item 4 section
+
+ Secondary tab item 5}>
+ Secondary tab item 5 section
+
+ Secondary tab item 6}>
+ Secondary tab item 6 section
+
+ Secondary tab item 7}>
+ Secondary tab item 7 section
+
+ Secondary tab item 8}>
+ Secondary tab item 8 section
+
+
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+ Tab item 7}>
+ Tab 7 section
+
+ Tab item 8}>
+ Tab 8 section
+
+ Tab item 9}>
+ Tab 9 section
+
+ Tab item 10}>
+ Tab 10 section
+
+ Tab item 11}>
+ Tab 11 section
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsToggledSeparateContent.tsx b/packages/react-core/src/components/Tabs/examples/TabsToggledSeparateContent.tsx
new file mode 100644
index 00000000000..80aaf1c72f9
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsToggledSeparateContent.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import { Tabs, Tab, TabContent, Button, Divider } from '@patternfly/react-core';
+
+export const TabsToggledSeparateContent: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isTab2Hidden, setisTab2Hidden] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const contentRef1 = React.createRef();
+ const contentRef2 = React.createRef();
+ const contentRef3 = React.createRef();
+
+ return (
+
+ setisTab2Hidden(!isTab2Hidden)}>{isTab2Hidden ? 'Show' : 'Hide'} tab 2
+
+
+
+ {!isTab2Hidden && (
+
+ )}
+
+
+
+
+ Tab 1 section
+
+ {!isTab2Hidden && (
+
+ Tab 2 section
+
+ )}
+
+ Tab 3 section
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsTooltipReactRef.tsx b/packages/react-core/src/components/Tabs/examples/TabsTooltipReactRef.tsx
new file mode 100644
index 00000000000..df7665c20ad
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsTooltipReactRef.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
+
+export const TabsTooltipReactRef: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ const tooltipRef = React.createRef();
+
+ return (
+
+
+ Users} aria-label="Tooltip ref content - users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Disabled} isDisabled>
+ Disabled
+
+ ARIA Disabled} isAriaDisabled>
+ ARIA Disabled
+
+ ARIA Disabled (Tooltip)}
+ isAriaDisabled
+ ref={tooltipRef}
+ aria-describedby="tooltip-ref1"
+ >
+ ARIA Disabled (Tooltip)
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsUncontrolled.tsx b/packages/react-core/src/components/Tabs/examples/TabsUncontrolled.tsx
new file mode 100644
index 00000000000..51fb4826278
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsUncontrolled.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Tooltip } from '@patternfly/react-core';
+
+export const TabsUncontrolled: React.FunctionComponent = () => {
+ const tooltip = (
+
+ );
+
+ return (
+ <>
+
+ Users} aria-label="Uncontrolled ref content - users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Disabled} isDisabled>
+ Disabled
+
+ ARIA Disabled} isAriaDisabled>
+ ARIA Disabled
+
+ ARIA Disabled (Tooltip)} isAriaDisabled tooltip={tooltip}>
+ ARIA Disabled (Tooltip)
+
+
+ >
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsUnmountingInvisibleChildren.tsx b/packages/react-core/src/components/Tabs/examples/TabsUnmountingInvisibleChildren.tsx
new file mode 100644
index 00000000000..ed5b275043c
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsUnmountingInvisibleChildren.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
+
+export const TabsUnmountingInvisibleChildren: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ return (
+
+ Tab item 1} aria-label="Invisible children tab 1">
+ Tab 1 section
+
+ Tab item 2} aria-label="Invisible children tab 2">
+ Tab 2 section
+
+ Tab item 3} aria-label="Invisible children tab 3">
+ Tab 3 section
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsVertical.tsx b/packages/react-core/src/components/Tabs/examples/TabsVertical.tsx
new file mode 100644
index 00000000000..3b05d926fd6
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsVertical.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText, Checkbox, Tooltip } from '@patternfly/react-core';
+
+export const TabsVertical: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isBox, setIsBox] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const toggleBox = (checked: boolean) => {
+ setIsBox(checked);
+ };
+
+ const tooltip = (
+
+ );
+
+ return (
+
+
+
+ Users
+
+ }
+ aria-label="Vertical example content users"
+ >
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Disabled} isDisabled>
+ Disabled
+
+ ARIA Disabled} isAriaDisabled>
+ ARIA Disabled
+
+ ARIA Disabled (Tooltip)} isAriaDisabled tooltip={tooltip}>
+ ARIA Disabled (Tooltip)
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandable.tsx b/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandable.tsx
new file mode 100644
index 00000000000..5b31c6f8469
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandable.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
+
+export const TabsVerticalExpandable: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ const [isExpanded, setIsExpanded] = React.useState(false);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ const onToggle = (isExpanded: boolean) => {
+ setIsExpanded(isExpanded);
+ };
+
+ return (
+
+ Users} aria-label="Vertical expandable content users">
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandableUncontrolled.tsx b/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandableUncontrolled.tsx
new file mode 100644
index 00000000000..ac01f6f7dc3
--- /dev/null
+++ b/packages/react-core/src/components/Tabs/examples/TabsVerticalExpandableUncontrolled.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
+export const TabsVerticalExpandableUncontrolled: React.FunctionComponent = () => {
+ const [activeTabKey, setActiveTabKey] = React.useState(0);
+ // Toggle currently active tab
+ const handleTabClick = (
+ event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
+ tabIndex: string | number
+ ) => {
+ setActiveTabKey(tabIndex);
+ };
+
+ return (
+
+ Users}
+ aria-label="Vertical expandable uncontrolled content users"
+ >
+ Users
+
+ Containers}>
+ Containers
+
+ Database}>
+ Database
+
+ Server}>
+ Server
+
+ System}>
+ System
+
+ Network}>
+ Network
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tabs/index.ts b/packages/react-core/src/components/Tabs/index.ts
index 83aa2d999e2..cf7db30a898 100644
--- a/packages/react-core/src/components/Tabs/index.ts
+++ b/packages/react-core/src/components/Tabs/index.ts
@@ -1,4 +1,5 @@
export * from './Tab';
+export * from './TabAction';
export * from './Tabs';
export * from './TabContent';
export * from './TabContentBody';
diff --git a/packages/react-core/src/components/Text/TextList.tsx b/packages/react-core/src/components/Text/TextList.tsx
index 85b4b84eeca..71b04420f1f 100644
--- a/packages/react-core/src/components/Text/TextList.tsx
+++ b/packages/react-core/src/components/Text/TextList.tsx
@@ -25,7 +25,7 @@ export const TextList: React.FunctionComponent = ({
const Component: any = component;
return (
-
+
{children}
);
diff --git a/packages/react-core/src/components/Text/TextListItem.tsx b/packages/react-core/src/components/Text/TextListItem.tsx
index 7d545480239..81b6eeb7c83 100644
--- a/packages/react-core/src/components/Text/TextListItem.tsx
+++ b/packages/react-core/src/components/Text/TextListItem.tsx
@@ -25,7 +25,7 @@ export const TextListItem: React.FunctionComponent = ({
const Component: any = component;
return (
-
+
{children}
);
diff --git a/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextList.test.tsx.snap b/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextList.test.tsx.snap
index 2586930f620..8c52190fe42 100644
--- a/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextList.test.tsx.snap
+++ b/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextList.test.tsx.snap
@@ -4,7 +4,6 @@ exports[`TextList should match snapshot (auto-generated) 1`] = `
diff --git a/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextListItem.test.tsx.snap b/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextListItem.test.tsx.snap
index c9cd241161c..a22cc9cfcb4 100644
--- a/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextListItem.test.tsx.snap
+++ b/packages/react-core/src/components/Text/__tests__/Generated/__snapshots__/TextListItem.test.tsx.snap
@@ -4,7 +4,6 @@ exports[`TextListItem should match snapshot (auto-generated) 1`] = `
ReactNode
diff --git a/packages/react-core/src/components/Text/__tests__/Text.test.tsx b/packages/react-core/src/components/Text/__tests__/Text.test.tsx
index ee40d979ff3..e0f8606f813 100644
--- a/packages/react-core/src/components/Text/__tests__/Text.test.tsx
+++ b/packages/react-core/src/components/Text/__tests__/Text.test.tsx
@@ -1,112 +1,101 @@
-import React from 'react';
-import { render } from '@testing-library/react';
-import { TextContent } from '../TextContent';
-import { Text, TextVariants } from '../Text';
-import { TextList, TextListVariants } from '../TextList';
-import { TextListItem, TextListItemVariants } from '../TextListItem';
-
-test('Text example should match snapshot', () => {
- const { asFragment } = render(
-
- Hello World
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, nulla
- nunc varius lectus, nec rutrum justo nibh eu lectus. Ut vulputate semper dui. Fusce erat odio, sollicitudin vel
- erat vel, interdum mattis neque. Sub works as well!
-
- Second level
-
- Curabitur accumsan turpis pharetra augue tincidunt blandit. Quisque condimentum maximus mi, sit
- amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. Suspendisse potenti. Etiam mattis sem
- rhoncus lacus dapibus facilisis. Donec at dignissim dui. Ut et neque nisl.
-
-
- In fermentum leo eu lectus mollis, quis dictum mi aliquet.
- Morbi eu nulla lobortis, lobortis est in, fringilla felis.
-
- Aliquam nec felis in sapien venenatis viverra fermentum nec lectus.
-
- In fermentum leo eu lectus mollis, quis dictum mi aliquet.
- Morbi eu nulla lobortis, lobortis est in, fringilla felis.
-
-
- Ut non enim metus.
-
- Third level
-
- Quisque ante lacus, malesuada ac auctor vitae, congue{' '}
-
- non ante
-
- . Phasellus lacus ex, semper ac tortor nec, fringilla condimentum orci. Fusce eu rutrum tellus.
-
-
- Donec blandit a lorem id convallis.
- Cras gravida arcu at diam gravida gravida.
- Integer in volutpat libero.
- Donec a diam tellus.
- Aenean nec tortor orci.
- Quisque aliquam cursus urna, non bibendum massa viverra eget.
- Vivamus maximus ultricies pulvinar.
-
-
- Ut venenatis, nisl scelerisque sollicitudin fermentum, quam libero hendrerit ipsum, ut blandit est tellus sit
- amet turpis.
-
-
- Quisque at semper enim, eu hendrerit odio. Etiam auctor nisl et justo sodales elementum. Maecenas
- ultrices lacus quis neque consectetur, et lobortis nisi molestie.
-
-
- Sed sagittis enim ac tortor maximus rutrum. Nulla facilisi. Donec mattis vulputate risus in luctus. Maecenas
- vestibulum interdum commodo.
-
-
- Web
-
- The part of the Internet that contains websites and web pages
-
- HTML
- A markup language for creating web pages
- CSS
- A technology to make HTML look better
-
-
- Suspendisse egestas sapien non felis placerat elementum. Morbi tortor nisl, suscipit sed mi sit amet, mollis
- malesuada nulla. Nulla facilisi. Nullam ac erat ante.
-
- Fourth level
-
- Nulla efficitur eleifend nisi, sit amet bibendum sapien fringilla ac. Mauris euismod metus a tellus laoreet, at
- elementum ex efficitur.
-
-
- Maecenas eleifend sollicitudin dui, faucibus sollicitudin augue cursus non. Ut finibus eleifend arcu ut
- vehicula. Mauris eu est maximus est porta condimentum in eu justo. Nulla id iaculis sapien.
-
- Sometimes you need small text to display things like date created
-
- Phasellus porttitor enim id metus volutpat ultricies. Ut nisi nunc, blandit sed dapibus at, vestibulum in felis.
- Etiam iaculis lorem ac nibh bibendum rhoncus. Nam interdum efficitur ligula sit amet ullamcorper. Etiam
- tristique, leo vitae porta faucibus, mi lacus laoreet metus, at cursus leo est vel tellus. Sed ac posuere est.
- Nunc ultricies nunc neque, vitae ultricies ex sodales quis. Aliquam eu nibh in libero accumsan pulvinar. Nullam
- nec nisl placerat, pretium metus vel, euismod ipsum. Proin tempor cursus nisl vel condimentum. Nam pharetra
- varius metus non pellentesque.
-
- Fifth level
-
- Aliquam sagittis rhoncus vulputate. Cras non luctus sem, sed tincidunt ligula. Vestibulum at nunc elit. Praesent
- aliquet ligula mi, in luctus elit volutpat porta. Phasellus molestie diam vel nisi sodales, a eleifend augue
- laoreet. Sed nec eleifend justo. Nam et sollicitudin odio.
-
- Sixth level
-
- Cras in nibh lacinia, venenatis nisi et, auctor urna. Donec pulvinar lacus sed diam dignissim, ut eleifend eros
- accumsan. Phasellus non tortor eros. Ut sed rutrum lacus. Etiam purus nunc, scelerisque quis enim vitae,
- malesuada ultrices turpis. Nunc vitae maximus purus, nec consectetur dui. Suspendisse euismod, elit vel rutrum
- commodo, ipsum tortor maximus dui, sed varius sapien odio vitae est. Etiam at cursus metus.
-
-
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { Text } from '../Text';
+
+test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('test-content').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders without class name by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass();
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+});
+
+test('Renders as "p" element by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'P');
+});
+
+test('Renders as "h1" element when component="h1"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 1 })).toBeVisible();
+});
+
+test('Renders as "h2" element when component="h2"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 2 })).toBeVisible();
+});
+
+test('Renders as "h3" element when component="h3"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 3 })).toBeVisible();
+});
+
+test('Renders as "h4" element when component="h4"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 4 })).toBeVisible();
+});
+
+test('Renders as "h5" element when component="h5"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 5 })).toBeVisible();
+});
+
+test('Renders as "h6" element when component="h6"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 6 })).toBeVisible();
+});
+
+test('Renders as "a" element when component="a"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'A');
+});
+
+test('Renders as "blockquote" element when component="blockquote"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'BLOCKQUOTE');
+});
+
+test('Renders as "pre" element when component="pre"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'PRE');
+});
+
+test('Renders without class name pf-m-visited by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass('pf-m-visited');
+});
+
+test('Renders with class name pf-m-visited when isVisited=true and component="a"', () => {
+ render(
+
+ Test
+
);
+ expect(screen.getByText('Test')).toHaveClass('pf-m-visited');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
expect(asFragment()).toMatchSnapshot();
});
diff --git a/packages/react-core/src/components/Text/__tests__/TextContent.text.tsx b/packages/react-core/src/components/Text/__tests__/TextContent.text.tsx
new file mode 100644
index 00000000000..38f526bb1a9
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/TextContent.text.tsx
@@ -0,0 +1,103 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { Text } from '../Text';
+
+describe('Button', () => {
+ test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('test-content').firstChild).toBeVisible();
+ });
+
+ test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+ });
+
+ test('Renders without class name by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass();
+ });
+
+ test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+ });
+
+ test('Renders as "p" element by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'P');
+ });
+
+ test('Renders as "h1" element when component="h1"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 1 })).toBeVisible();
+ });
+
+ test('Renders as "h2" element when component="h2"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 2 })).toBeVisible();
+ });
+
+ test('Renders as "h3" element when component="h3"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 3 })).toBeVisible();
+ });
+
+ test('Renders as "h4" element when component="h4"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 4 })).toBeVisible();
+ });
+
+ test('Renders as "h5" element when component="h5"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 5 })).toBeVisible();
+ });
+
+ test('Renders as "h6" element when component="h6"', () => {
+ render(Test );
+ expect(screen.getByRole('heading', { level: 6 })).toBeVisible();
+ });
+
+ test('Renders as "a" element when component="a"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'A');
+ });
+
+ test('Renders as "blockquote" element when component="blockquote"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'BLOCKQUOTE');
+ });
+
+ test('Renders as "pre" element when component="pre"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'PRE');
+ });
+
+ test('Renders without class name pf-m-visited by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass('pf-m-visited');
+ });
+
+ test('Renders with class name pf-m-visited when isVisited=true and component="a"', () => {
+ render(
+
+ Test
+
+ );
+ expect(screen.getByText('Test')).toHaveClass('pf-m-visited');
+ });
+
+ test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+ });
+
+ test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+ });
+});
diff --git a/packages/react-core/src/components/Text/__tests__/TextList.test.tsx b/packages/react-core/src/components/Text/__tests__/TextList.test.tsx
new file mode 100644
index 00000000000..6bbfbb11726
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/TextList.test.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { TextList } from '../TextList';
+
+test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('test-list').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders without class name by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass();
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+});
+
+test('Renders as "ul" element by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'UL');
+});
+
+test('Renders as "ol" element when component="ol"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'OL');
+});
+
+test('Renders as "dl" element when component="dl"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'DL');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Text/__tests__/TextListItem.test.tsx b/packages/react-core/src/components/Text/__tests__/TextListItem.test.tsx
new file mode 100644
index 00000000000..34f7fbb9039
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/TextListItem.test.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { render, screen } from '@testing-library/react';
+import { TextListItem } from '../TextListItem';
+
+test('Renders without children', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('test-list-item').firstChild).toBeVisible();
+});
+
+test('Renders children', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toBeVisible();
+});
+
+test('Renders without class name by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).not.toHaveClass();
+});
+
+test('Renders with custom class name when className prop is provided', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveClass('custom-class');
+});
+
+test('Renders as "li" element by default', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'LI');
+});
+
+test('Renders as "dt" element when component="dt"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'DT');
+});
+
+test('Renders as "dd" element when component="dd"', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveProperty('nodeName', 'DD');
+});
+
+test('Renders with inherited element props spread to the component', () => {
+ render(Test );
+ expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
+});
+
+test('Matches the snapshot', () => {
+ const { asFragment } = render(Test );
+ expect(asFragment()).toMatchSnapshot();
+});
diff --git a/packages/react-core/src/components/Text/__tests__/__snapshots__/Text.test.tsx.snap b/packages/react-core/src/components/Text/__tests__/__snapshots__/Text.test.tsx.snap
index 3d108df3f66..a260407f116 100644
--- a/packages/react-core/src/components/Text/__tests__/__snapshots__/Text.test.tsx.snap
+++ b/packages/react-core/src/components/Text/__tests__/__snapshots__/Text.test.tsx.snap
@@ -1,334 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Text example should match snapshot 1`] = `
+exports[`Matches the snapshot 1`] = `
-
-
- Hello World
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla accumsan, metus ultrices eleifend gravida, nulla nunc varius lectus, nec rutrum justo nibh eu lectus. Ut vulputate semper dui. Fusce erat odio, sollicitudin vel erat vel, interdum mattis neque. Sub works as well!
-
-
- Second level
-
-
- Curabitur accumsan turpis pharetra
-
- augue tincidunt
-
- blandit. Quisque condimentum maximus mi, sit amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. Suspendisse potenti. Etiam mattis sem rhoncus lacus dapibus facilisis. Donec at dignissim dui. Ut et neque nisl.
-
-
-
- In fermentum leo eu lectus mollis, quis dictum mi aliquet.
-
-
- Morbi eu nulla lobortis, lobortis est in, fringilla felis.
-
-
- Aliquam nec felis in sapien venenatis viverra fermentum nec lectus.
-
-
- In fermentum leo eu lectus mollis, quis dictum mi aliquet.
-
-
- Morbi eu nulla lobortis, lobortis est in, fringilla felis.
-
-
-
-
- Ut non enim metus.
-
-
-
- Third level
-
-
- Quisque ante lacus, malesuada ac auctor vitae, congue
-
- non ante
-
- . Phasellus lacus ex, semper ac tortor nec, fringilla condimentum orci. Fusce eu rutrum tellus.
-
-
-
- Donec blandit a lorem id convallis.
-
-
- Cras gravida arcu at diam gravida gravida.
-
-
- Integer in volutpat libero.
-
-
- Donec a diam tellus.
-
-
- Aenean nec tortor orci.
-
-
- Quisque aliquam cursus urna, non bibendum massa viverra eget.
-
-
- Vivamus maximus ultricies pulvinar.
-
-
-
- Ut venenatis, nisl scelerisque sollicitudin fermentum, quam libero hendrerit ipsum, ut blandit est tellus sit amet turpis.
-
-
- Quisque at semper enim, eu hendrerit odio. Etiam auctor nisl et
-
- justo sodales
-
- elementum. Maecenas ultrices lacus quis neque consectetur, et lobortis nisi molestie.
-
-
- Sed sagittis enim ac tortor maximus rutrum. Nulla facilisi. Donec mattis vulputate risus in luctus. Maecenas vestibulum interdum commodo.
-
-
-
- Web
-
-
- The part of the Internet that contains websites and web pages
-
-
- HTML
-
-
- A markup language for creating web pages
-
-
- CSS
-
-
- A technology to make HTML look better
-
-
-
- Suspendisse egestas sapien non felis placerat elementum. Morbi tortor nisl, suscipit sed mi sit amet, mollis malesuada nulla. Nulla facilisi. Nullam ac erat ante.
-
-
- Fourth level
-
-
- Nulla efficitur eleifend nisi, sit amet bibendum sapien fringilla ac. Mauris euismod metus a tellus laoreet, at elementum ex efficitur.
-
-
- Maecenas eleifend sollicitudin dui, faucibus sollicitudin augue cursus non. Ut finibus eleifend arcu ut vehicula. Mauris eu est maximus est porta condimentum in eu justo. Nulla id iaculis sapien.
-
-
- Sometimes you need small text to display things like date created
-
-
- Phasellus porttitor enim id metus volutpat ultricies. Ut nisi nunc, blandit sed dapibus at, vestibulum in felis. Etiam iaculis lorem ac nibh bibendum rhoncus. Nam interdum efficitur ligula sit amet ullamcorper. Etiam tristique, leo vitae porta faucibus, mi lacus laoreet metus, at cursus leo est vel tellus. Sed ac posuere est. Nunc ultricies nunc neque, vitae ultricies ex sodales quis. Aliquam eu nibh in libero accumsan pulvinar. Nullam nec nisl placerat, pretium metus vel, euismod ipsum. Proin tempor cursus nisl vel condimentum. Nam pharetra varius metus non pellentesque.
-
-
- Fifth level
-
-
- Aliquam sagittis rhoncus vulputate. Cras non luctus sem, sed tincidunt ligula. Vestibulum at nunc elit. Praesent aliquet ligula mi, in luctus elit volutpat porta. Phasellus molestie diam vel nisi sodales, a eleifend augue laoreet. Sed nec eleifend justo. Nam et sollicitudin odio.
-
-
- Sixth level
-
-
- Cras in nibh lacinia, venenatis nisi et, auctor urna. Donec pulvinar lacus sed diam dignissim, ut eleifend eros accumsan. Phasellus non tortor eros. Ut sed rutrum lacus. Etiam purus nunc, scelerisque quis enim vitae, malesuada ultrices turpis. Nunc vitae maximus purus, nec consectetur dui. Suspendisse euismod, elit vel rutrum commodo, ipsum tortor maximus dui, sed varius sapien odio vitae est. Etiam at cursus metus.
-
-
+ Test
+
`;
diff --git a/packages/react-core/src/components/Text/__tests__/__snapshots__/TextContent.text.tsx.snap b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextContent.text.tsx.snap
new file mode 100644
index 00000000000..0b2bdb2cd6a
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextContent.text.tsx.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Button Matches the snapshot 1`] = `
+
+
+ Test
+
+
+`;
diff --git a/packages/react-core/src/components/Text/__tests__/__snapshots__/TextList.test.tsx.snap b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextList.test.tsx.snap
new file mode 100644
index 00000000000..511aedf785f
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextList.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+
+`;
diff --git a/packages/react-core/src/components/Text/__tests__/__snapshots__/TextListItem.test.tsx.snap b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextListItem.test.tsx.snap
new file mode 100644
index 00000000000..98bbdbd6a00
--- /dev/null
+++ b/packages/react-core/src/components/Text/__tests__/__snapshots__/TextListItem.test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Matches the snapshot 1`] = `
+
+
+ Test
+
+
+`;
diff --git a/packages/react-core/src/components/TextArea/examples/TextArea.md b/packages/react-core/src/components/TextArea/examples/TextArea.md
index 235dfc24d63..4f80fdb6e52 100644
--- a/packages/react-core/src/components/TextArea/examples/TextArea.md
+++ b/packages/react-core/src/components/TextArea/examples/TextArea.md
@@ -9,300 +9,52 @@ propComponents: ['TextArea']
### Basic
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
-class SimpleTextArea extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: ''
- };
-
- this.handleTextAreaChange = value => {
- this.setState({ value });
- };
- }
-
- render() {
- const { value } = this.state;
-
- return ;
- }
-}
+```ts file="./TextAreaBasic.tsx"
```
### Invalid
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
-class InvalidTextArea extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: ''
- };
-
- this.handleInvalidTextAreaChange = value => {
- this.setState({ value });
- };
- }
-
- render() {
- const { value } = this.state;
-
- return (
-
- );
- }
-}
+```ts file="./TextAreaInvalid.tsx"
```
### Validated
-```js
-import React from 'react';
-import { Form, FormGroup, TextArea } from '@patternfly/react-core';
-
-class InvalidTextArea extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: '',
- invalidText: 'You must have something to say',
- validated: 'default',
- helperText: 'Share your thoughts.'
- };
-
- this.simulateNetworkCall = callback => {
- setTimeout(callback, 2000);
- };
-
- this.handleTextAreaChange = value => {
- this.setState(
- {
- value,
- validated: 'default',
- helperText: 'Validating...'
- },
- this.simulateNetworkCall(() => {
- if (value && value.length > 0) {
- if (value.length >= 10) {
- this.setState({ validated: 'success', helperText: 'Thanks for your comments!' });
- } else {
- this.setState({
- validated: 'error',
- invalidText: 'Your being too brief, please enter at least 10 characters.'
- });
- }
- } else {
- this.setState({ validated: 'warning', helperText: 'You must have something to say' });
- }
- })
- );
- };
- }
-
- render() {
- const { value, validated, helperText, invalidText } = this.state;
-
- return (
-
- );
- }
-}
+```ts file="./TextAreaValidated.tsx"
```
-### Vertically resizable text area
-
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
+### Vertically resizable
-class VerticalResizeTextArea extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: ''
- };
-
- this.handleTextAreaChange = value => {
- this.setState({ value });
- };
- }
-
- render() {
- const { value } = this.state;
-
- return (
-
- );
- }
-}
+```ts file="./TextAreaVerticallyResizable.tsx"
```
-### Horizontally resizable text area
-
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
-class HorizontalResizeTextArea extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: ''
- };
-
- this.handleTextAreaChange = value => {
- this.setState({ value });
- };
- }
-
- render() {
- const { value } = this.state;
+### Horizontally resizable
- return (
-
- );
- }
-}
+```ts file="./TextAreaHorizontallyResizable.tsx"
```
### Uncontrolled
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
- ;
+```ts file="./TextAreaUncontrolled.tsx"
```
### Disabled
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
- ;
+```ts file="./TextAreaDisabled.tsx"
```
### Read only
-```js
-import React from 'react';
-import { Checkbox, TextArea } from '@patternfly/react-core';
-
-const ReadOnlyTextArea = () => {
- const [isPlainChecked, setIsPlainChecked] = React.useState(false);
-
- return (
-
-
- setIsPlainChecked(checked)}
- />
-
-
-
- );
-};
+```ts file="./TextAreaReadOnly.tsx"
```
### Auto resizing
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
- ;
+```ts file="./TextAreaAutoResizing.tsx"
```
### Icon sprite variants
**Note:** The icons for the success, invalid, calendar, etc. variations in form control elements are applied as background images to the form element. By default, the image URLs for these icons are data URIs. However, there may be cases where data URIs are not ideal, such as in an application with a content security policy that disallows data URIs for security reasons. The `isIconSprite` variation changes the icon source to an external SVG file that serves as a sprite for all of the supported icons.
-```js
-import React from 'react';
-import { TextArea } from '@patternfly/react-core';
-
-IconSpriteTextArea = () => {
- const [success, setSuccess] = React.useState('');
- const [warning, setWarning] = React.useState('');
- const [error, setError] = React.useState('');
-
- return (
- <>
- setSuccess(value)}
- aria-label="success icon sprite text area example"
- />
-
-
- setWarning(value)}
- aria-label="warning icon sprite text input example"
- />
-
-
- setError(value)}
- aria-label="error icon sprite text area example"
- />
- >
- );
-};
+```ts isBeta file="./TextAreaIconSprite.tsx"
```
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaAutoResizing.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaAutoResizing.tsx
new file mode 100644
index 00000000000..ff4f536b60b
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaAutoResizing.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaAutoResizing: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ return (
+ setValue(value)}
+ aria-label="auto resizing text area example"
+ autoResize
+ />
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaBasic.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaBasic.tsx
new file mode 100644
index 00000000000..228602248df
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaBasic.tsx
@@ -0,0 +1,7 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaBasic: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ return setValue(value)} aria-label="text area example" />;
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaDisabled.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaDisabled.tsx
new file mode 100644
index 00000000000..cb5b455a4b0
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaDisabled.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaDisabled: React.FunctionComponent = () => (
+
+);
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaHorizontallyResizable.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaHorizontallyResizable.tsx
new file mode 100644
index 00000000000..7aa543459b4
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaHorizontallyResizable.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaHorizontallyResizable: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ return (
+ setValue(value)}
+ resizeOrientation="horizontal"
+ aria-label="text area horizontal resize example"
+ />
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaIconSprite.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaIconSprite.tsx
new file mode 100644
index 00000000000..c63a9d8938c
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaIconSprite.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaIconSprite: React.FunctionComponent = () => {
+ const [success, setSuccess] = React.useState('');
+ const [warning, setWarning] = React.useState('');
+ const [error, setError] = React.useState('');
+
+ return (
+ <>
+ setSuccess(value)}
+ value={success}
+ aria-label="success icon sprite text area example"
+ />
+
+
+ setWarning(value)}
+ value={warning}
+ aria-label="warning icon sprite text area example"
+ />
+
+
+ setError(value)}
+ value={error}
+ aria-label="error icon sprite text area example"
+ />
+ >
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaInvalid.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaInvalid.tsx
new file mode 100644
index 00000000000..765f29b9569
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaInvalid.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaInvalid: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ return (
+ setValue(value)}
+ isRequired
+ validated={'error'}
+ aria-label="invalid text area example"
+ />
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaReadOnly.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaReadOnly.tsx
new file mode 100644
index 00000000000..bf9da8f74a9
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaReadOnly.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { Checkbox, TextArea } from '@patternfly/react-core';
+
+export const TextAreaReadOnly: React.FunctionComponent = () => {
+ const [isPlainChecked, setIsPlainChecked] = React.useState(false);
+
+ return (
+
+
+ setIsPlainChecked(checked)}
+ />
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaUncontrolled.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaUncontrolled.tsx
new file mode 100644
index 00000000000..f2a75725e2f
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaUncontrolled.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const TextAreaUncontrolled: React.FunctionComponent = () => (
+
+);
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaValidated.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaValidated.tsx
new file mode 100644
index 00000000000..58aa1c2ee46
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaValidated.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Form, FormGroup, TextArea } from '@patternfly/react-core';
+
+export const TextAreaValidated: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ const [invalidText, setInvalidText] = React.useState('You must have something to say');
+ const [validated, setValidated] = React.useState<'default' | 'error' | 'warning' | 'success' | undefined>('default');
+ const [helperText, setHelperText] = React.useState('Share your thoughts.');
+ const simulateNetworkCall = (callback: Function) => {
+ setTimeout(callback, 2000);
+ };
+
+ const handleTextAreaChange = (value: string) => {
+ setValue(value);
+ setValidated('default');
+ setHelperText('Validating...');
+ simulateNetworkCall(() => {
+ if (value.length > 0) {
+ if (value.length >= 10) {
+ setValidated('success');
+ setHelperText('Thanks for your comments!');
+ } else {
+ setValidated('error');
+ setInvalidText("You're being too brief, please enter at least 10 characters.");
+ }
+ } else {
+ setValidated('warning');
+ setHelperText('You must have something to say');
+ }
+ });
+ };
+ return (
+
+
+ handleTextAreaChange(value)}
+ isRequired
+ validated={validated}
+ aria-label="invalid text area example"
+ />
+
+
+ );
+};
diff --git a/packages/react-core/src/components/TextArea/examples/TextAreaVerticallyResizable.tsx b/packages/react-core/src/components/TextArea/examples/TextAreaVerticallyResizable.tsx
new file mode 100644
index 00000000000..769dcd83573
--- /dev/null
+++ b/packages/react-core/src/components/TextArea/examples/TextAreaVerticallyResizable.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { TextArea } from '@patternfly/react-core';
+
+export const VerticalResizeTextArea: React.FunctionComponent = () => {
+ const [value, setValue] = React.useState('');
+ return (
+ setValue(value)}
+ resizeOrientation="vertical"
+ aria-label="text area vertical resize example"
+ />
+ );
+};
diff --git a/packages/react-core/src/components/TimePicker/TimePicker.tsx b/packages/react-core/src/components/TimePicker/TimePicker.tsx
index ea83d55ae75..6506ef3b150 100644
--- a/packages/react-core/src/components/TimePicker/TimePicker.tsx
+++ b/packages/react-core/src/components/TimePicker/TimePicker.tsx
@@ -71,6 +71,8 @@ export interface TimePickerProps
isOpen?: boolean;
/** Handler invoked each time the open state of time picker updates */
setIsOpen?: (isOpen?: boolean) => void;
+ /** z-index of the time picker */
+ zIndex?: number;
}
interface TimePickerState {
@@ -108,7 +110,8 @@ export class TimePicker extends React.Component {}
+ setIsOpen: () => {},
+ zIndex: 9999
};
constructor(props: TimePickerProps) {
@@ -140,6 +143,8 @@ export class TimePicker extends React.Component) => {
- const { timeRegex } = this.state;
- const { delimiter, is24Hour, includeSeconds } = this.props;
- const time = parseTime(event.currentTarget.value, timeRegex, delimiter, !is24Hour, includeSeconds);
-
- this.setState({
- isInvalid: !this.isValid(time)
- });
- };
-
render() {
const {
'aria-label': ariaLabel,
@@ -440,7 +449,7 @@ export class TimePicker extends React.Component
diff --git a/packages/react-core/src/components/TimePicker/__tests__/TimePicker.test.tsx b/packages/react-core/src/components/TimePicker/__tests__/TimePicker.test.tsx
index 9087f63c91a..c94c8b45b73 100644
--- a/packages/react-core/src/components/TimePicker/__tests__/TimePicker.test.tsx
+++ b/packages/react-core/src/components/TimePicker/__tests__/TimePicker.test.tsx
@@ -107,24 +107,27 @@ describe('TimePicker', () => {
});
test('should be invalid after onBlur', async () => {
- const validateTime = (_time: string) => {
- return false;
- };
const user = userEvent.setup();
render(
<>
Other element
-
+
>
);
- await user.type(screen.getByLabelText('time picker'), '01:00');
+ await user.type(screen.getByLabelText('time picker'), '14:00');
expect(screen.queryByText('Invalid time format')).toBeNull();
await user.click(screen.getByText('Other element'));
expect(screen.getByText('Invalid time format')).toBeInTheDocument();
});
+
+ test('should be invalid when invalid time prop is passed', () => {
+ render( );
+
+ expect(screen.getByText('Invalid time format')).toBeInTheDocument();
+ });
});
describe('test includeSeconds', () => {
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroup.md b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroup.md
index ba0723bb5c2..c9ae050331a 100644
--- a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroup.md
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroup.md
@@ -14,207 +14,21 @@ import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square
## Examples
### Default with multiple selectable
-```js
-import React from 'react';
-import { ToggleGroup, ToggleGroupItem, Button, Stack, StackItem } from '@patternfly/react-core';
-
-class DefaultToggleGroupExample extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isSelected: {
- first: false,
- second: false,
- disableAll: false
- }
- };
- this.handleItemClick = (isSelected, event) => {
- const id = event.currentTarget.id;
- this.setState(prevState => {
- prevState.isSelected[id] = isSelected;
- return {
- isSelected: prevState.isSelected
- };
- });
- };
- this.disableAllClick = () => {
- this.setState(prevState => ({ disableAll: !prevState.disableAll }));
- };
- }
-
- render() {
- const { isSelected } = this.state;
-
- return (
-
-
-
- {this.state.disableAll ? "Enable back" : "Disable all"}
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
+```ts file="./ToggleGroupDefaultMultiple.tsx"
```
### Default with single selectable
-```js
-import React from 'react';
-import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
-
-class DefaultAsRadioToggleGroupExample extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isSelected: ""
- };
- this.handleItemClick = (isSelected, event) => {
- const id = event.currentTarget.id;
- this.setState({ isSelected: id });
- };
- }
-
- render() {
- const { isSelected } = this.state;
-
- return (
-
-
-
-
-
- );
- }
-}
+```ts file="./ToggleGroupDefaultSingle.tsx"
```
### Icons
-```js
-import React from 'react';
-import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
-import UndoIcon from '@patternfly/react-icons/dist/esm/icons/undo-icon';
-import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
-import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square-icon';
-
-class IconToggleGroupExample extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isSelected: {
- "icons-1": false,
- "icons-2": false,
- "icons-3": true
- }
- };
- this.handleItemClick = (isSelected, event) => {
- const id = event.currentTarget.id;
- this.setState(prevState => {
- prevState.isSelected[id] = isSelected;
- return {
- isSelected: prevState.isSelected
- };
- });
- };
- }
-
- render() {
- const { isSelected } = this.state;
- return (
-
- } aria-label="copy icon button" buttonId="icons-1" isSelected={isSelected["icons-1"]} onChange={this.handleItemClick} />
- } aria-label="undo icon button" buttonId="icons-2" isSelected={isSelected["icons-2"]} onChange={this.handleItemClick} />
- } aria-label="share square icon button" buttonId="icons-3" isSelected={isSelected["icons-3"]} onChange={this.handleItemClick} />
-
- );
- }
-}
+```ts file="./ToggleGroupIcon.tsx"
```
### Text and icons
-```js
-import React from 'react';
-import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
-import UndoIcon from '@patternfly/react-icons/dist/esm/icons/undo-icon';
-import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
-import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square-icon';
-
-class TextIconToggleGroupExample extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isSelected: {
- "text-icons-1": false,
- "text-icons-2": false,
- "text-icons-3": true
- }
- };
- this.handleItemClick = (isSelected, event) => {
- const id = event.currentTarget.id;
- this.setState(prevState => {
- prevState.isSelected[id] = isSelected;
- return {
- isSelected: prevState.isSelected
- };
- });
- };
- }
-
- render() {
- const { isSelected } = this.state;
- return (
-
- } text="Copy" aria-label="copy icon button" buttonId="text-icons-1" isSelected={isSelected["text-icons-1"]} onChange={this.handleItemClick} />
- } text="Undo" aria-label="undo icon button" buttonId="text-icons-2" isSelected={isSelected["text-icons-2"]} onChange={this.handleItemClick} />
- } text="Share" aria-label="share square icon button" buttonId="text-icons-3" isSelected={isSelected["text-icons-3"]} onChange={this.handleItemClick} />
-
- );
- }
-}
+```ts file="./ToggleGroupTextIcon.tsx"
```
### Compact variant
-```js
-import React from 'react';
-import { ToggleGroup, ToggleGroupItem, ToggleGroupVariant } from '@patternfly/react-core';
-
-class CompactToggleGroupExample extends React.Component {
-constructor(props) {
- super(props);
- this.state = {
- isSelected: {
- "compact-1": false,
- "compact-2": false
- }
- };
- this.handleItemClick = (isSelected, event) => {
- const id = event.currentTarget.id;
- this.setState(prevState => {
- prevState.isSelected[id] = isSelected;
- return {
- isSelected: prevState.isSelected
- };
- });
- };
- }
-
- render() {
- const { isSelected } = this.state;
-
- return (
-
-
-
-
-
- );
- }
-}
+```ts file="./ToggleGroupCompact.tsx"
```
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupCompact.tsx b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupCompact.tsx
new file mode 100644
index 00000000000..ea64b90b02b
--- /dev/null
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupCompact.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
+
+export const ToggleGroupCompact: React.FunctionComponent = () => {
+ const [isSelected, setIsSelected] = React.useState({
+ 'toggle-group-compact-1': false,
+ 'toggle-group-compact-2': false
+ });
+ const handleItemClick = (isSelected: boolean, event: React.MouseEvent | React.KeyboardEvent | MouseEvent) => {
+ const id = event.currentTarget.id;
+ setIsSelected(prevIsSelected => ({ ...prevIsSelected, [id]: isSelected }));
+ };
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultMultiple.tsx b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultMultiple.tsx
new file mode 100644
index 00000000000..aa05d672585
--- /dev/null
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultMultiple.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { ToggleGroup, ToggleGroupItem, Button, Stack, StackItem } from '@patternfly/react-core';
+
+export const ToggleGroupDefaultMultiple: React.FunctionComponent = () => {
+ const [isSelected, setIsSelected] = React.useState({
+ 'toggle-group-multiple-1': false,
+ 'toggle-group-multiple-2': false
+ });
+ const [disableAll, setDisableAll] = React.useState(false);
+ const handleItemClick = (isSelected: boolean, event: React.MouseEvent | React.KeyboardEvent | MouseEvent) => {
+ const id = event.currentTarget.id;
+ setIsSelected(prevIsSelected => ({ ...prevIsSelected, [id]: isSelected }));
+ };
+ const disableAllClick = () => {
+ setDisableAll(!disableAll);
+ };
+ return (
+
+
+ {disableAll ? 'Enable back' : 'Disable all'}
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultSingle.tsx b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultSingle.tsx
new file mode 100644
index 00000000000..608c8d93503
--- /dev/null
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupDefaultSingle.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
+
+export const ToggleGroupDefaultSingle: React.FunctionComponent = () => {
+ const [isSelected, setIsSelected] = React.useState('');
+ const handleItemClick = (isSelected: boolean, event: React.MouseEvent | React.KeyboardEvent | MouseEvent) => {
+ const id = event.currentTarget.id;
+ setIsSelected(id);
+ };
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupIcon.tsx b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupIcon.tsx
new file mode 100644
index 00000000000..7bb0eef13f1
--- /dev/null
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupIcon.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
+import UndoIcon from '@patternfly/react-icons/dist/esm/icons/undo-icon';
+import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
+import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square-icon';
+
+export const ToggleGroupIcon: React.FunctionComponent = () => {
+ const [isSelected, setIsSelected] = React.useState({
+ 'toggle-group-icons-1': false,
+ 'toggle-group-icons-2': false,
+ 'toggle-group-icons-3': true
+ });
+ const handleItemClick = (isSelected: boolean, event: React.MouseEvent | React.KeyboardEvent | MouseEvent) => {
+ const id = event.currentTarget.id;
+ setIsSelected(prevIsSelected => ({ ...prevIsSelected, [id]: isSelected }));
+ };
+ return (
+
+ }
+ aria-label="copy"
+ buttonId="toggle-group-icons-1"
+ isSelected={isSelected['toggle-group-icons-1']}
+ onChange={handleItemClick}
+ />
+ }
+ aria-label="undo"
+ buttonId="toggle-group-icons-2"
+ isSelected={isSelected['toggle-group-icons-2']}
+ onChange={handleItemClick}
+ />
+ }
+ aria-label="share square"
+ buttonId="toggle-group-icons-3"
+ isSelected={isSelected['toggle-group-icons-3']}
+ onChange={handleItemClick}
+ />
+
+ );
+};
diff --git a/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupTextIcon.tsx b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupTextIcon.tsx
new file mode 100644
index 00000000000..2755727da5f
--- /dev/null
+++ b/packages/react-core/src/components/ToggleGroup/examples/ToggleGroupTextIcon.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
+import UndoIcon from '@patternfly/react-icons/dist/esm/icons/undo-icon';
+import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
+import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square-icon';
+
+export const ToggleGroupIcon: React.FunctionComponent = () => {
+ const [isSelected, setIsSelected] = React.useState({
+ 'text-icons-1': false,
+ 'text-icons-2': false,
+ 'text-icons-3': true
+ });
+ const handleItemClick = (isSelected: boolean, event: React.MouseEvent | React.KeyboardEvent | MouseEvent) => {
+ const id = event.currentTarget.id;
+ setIsSelected(prevIsSelected => ({ ...prevIsSelected, [id]: isSelected }));
+ };
+ return (
+
+ }
+ text="Copy"
+ buttonId="toggle-group-text-icons-1"
+ isSelected={isSelected['toggle-group-text-icons-1']}
+ onChange={handleItemClick}
+ />
+ }
+ text="Undo"
+ buttonId="toggle-group-text-icons-2"
+ isSelected={isSelected['toggle-group-text-icons-2']}
+ onChange={handleItemClick}
+ />
+ }
+ text="Share"
+ buttonId="toggle-group-text-icons-3"
+ isSelected={isSelected['toggle-group-text-icons-3']}
+ onChange={handleItemClick}
+ />
+
+ );
+};
diff --git a/packages/react-core/src/components/Tooltip/examples/Tooltip.md b/packages/react-core/src/components/Tooltip/examples/Tooltip.md
index 1d015eebe51..485b8e3be82 100644
--- a/packages/react-core/src/components/Tooltip/examples/Tooltip.md
+++ b/packages/react-core/src/components/Tooltip/examples/Tooltip.md
@@ -13,68 +13,17 @@ import './TooltipExamples.css';
### Basic
-```js
-import React from 'react';
-import { Tooltip } from '@patternfly/react-core';
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
- }
- >
-
- I have a tooltip!
-
-
-;
+```ts file="./TooltipBasic.tsx"
```
### Tooltip react ref
-```js
-import React from 'react';
-import { Tooltip } from '@patternfly/react-core';
-
-TooltipReactRef = () => {
- const tooltipRef = React.useRef();
- return (
-
-
- Tooltip attached via react ref
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
-
- }
- reference={tooltipRef}
- />
-
- );
-};
+```ts file="./TooltipReactRef.tsx"
```
### Tooltip selector ref
-```js
-import React from 'react';
-import { Tooltip } from '@patternfly/react-core';
-
-
-
- Tooltip attached via selector ref
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
- }
- reference={() => document.getElementById('tooltip-selector')}
- />
-;
+```ts file="./TooltipSelectorRef.tsx"
```
### On icon with dynamic content
@@ -83,239 +32,10 @@ When the tooltip is used as a wrapper and its content will dynamically update, t
When using a React or selector ref with a tooltip that has dynamic content, the `aria` and `aria-live` props do not need to be manually passed in.
-```js
-import React from 'react';
-import { Tooltip, Button } from '@patternfly/react-core';
-import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
-
-IconExample = () => {
- const [showSuccessContent, setShowSuccessContent] = React.useState(false);
- const copyText = 'Copy to clipboard';
- const doneCopyText = 'Successfully copied to clipboard!';
- const [content, setContent] = React.useState(copyText);
- return (
-
-
- setShowSuccessContent(!showSuccessContent)}
- >
-
-
-
-
- );
-};
+```ts file="./TooltipIcon.tsx"
```
### Options
-```js
-import React from 'react';
-import { Button, Tooltip, Checkbox, Select, SelectOption, TextInput } from '@patternfly/react-core';
-
-OptionsTooltip = () => {
- const [trigger, setTrigger] = React.useState(['mouseenter', 'focus']);
- const [isVisible, setIsVisible] = React.useState(true);
- const [contentLeftAligned, setContentLeftAligned] = React.useState(false);
- const [enableFlip, setEnableFlip] = React.useState(true);
- const [position, setPosition] = React.useState('top');
- const [positionSelectOpen, setPositionSelectOpen] = React.useState(false);
- const [flipSelectOpen, setFlipSelectOpen] = React.useState(false);
- const [flipBehavior, setFlipBehavior] = React.useState('flip');
- const [entryDelayInput, setEntryDelayInput] = React.useState(0);
- const [exitDelayInput, setExitDelayInput] = React.useState(0);
- const [animationDuration, setAnimationDuration] = React.useState(300);
- const tipBoxRef = React.useRef(null);
-
- const scrollToRef = ref => {
- if (ref && ref.current) {
- ref.current.scrollTop = 400;
- ref.current.scrollLeft = 300;
- }
- };
-
- React.useEffect(() => {
- scrollToRef(tipBoxRef);
- }, []);
-
- return (
- <>
-
-
- {
- let updatedTrigger;
- checked && (updatedTrigger = trigger.concat('mouseenter'));
- !checked && (updatedTrigger = trigger.filter(t => t !== 'mouseenter'));
- setIsVisible(false);
- setTrigger(updatedTrigger);
- }}
- aria-label="trigger: mouseenter"
- id="trigger_mouseenter"
- />
- {
- let updatedTrigger;
- checked && (updatedTrigger = trigger.concat('focus'));
- !checked && (updatedTrigger = trigger.filter(t => t !== 'focus'));
- setIsVisible(false);
- setTrigger(updatedTrigger);
- }}
- aria-label="trigger: focus"
- id="trigger_focus"
- />
- {
- let updatedTrigger;
- checked && (updatedTrigger = trigger.concat('click'));
- !checked && (updatedTrigger = trigger.filter(t => t !== 'click'));
- setIsVisible(false);
- setTimeout(() => setTrigger(updatedTrigger));
- }}
- aria-label="trigger: click"
- id="trigger_click"
- />
- {
- let updatedTrigger;
- checked && (updatedTrigger = trigger.concat('manual'));
- !checked && (updatedTrigger = trigger.filter(t => t !== 'manual'));
- setIsVisible(false);
- setTrigger(updatedTrigger);
- }}
- aria-label="trigger: manual"
- id="trigger_manual"
- />
-
-
- setContentLeftAligned(checked)}
- aria-label="content left-aligned"
- id="content_left_aligned"
- />
-
-
- setEnableFlip(checked)}
- aria-label="enableFlip"
- id="enable_flip"
- />
-
-
- position (will flip if enableFlip is true). The 'auto' position requires enableFlip to be set to true.
- setPositionSelectOpen(!positionSelectOpen)}
- onSelect={(event, selection) => {
- setPosition(selection);
- setPositionSelectOpen(false);
- }}
- isOpen={positionSelectOpen}
- selections={position}
- menuAppendTo={() => document.body}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- setIsVisible(checked)}
- aria-label="isVisible"
- id="is_visible"
- />
-
-
- Entry delay (ms){' '}
- setEntryDelayInput(val)}
- aria-label="entry delay"
- />
- Exit delay (ms) setExitDelayInput(val)}
- aria-label="exit delay"
- />
- Animation duration (ms){' '}
- setAnimationDuration(val)}
- aria-label="animation duration"
- />
-
-
- flip behavior examples (enableFlip has to be true). "flip" will try to flip the tooltip to the opposite of the
- starting position. The second option ensures that there are 3 escape positions for every possible starting
- position (default). This setting is ignored if position prop is set to 'auto'.
- setFlipSelectOpen(!flipSelectOpen)}
- onSelect={(event, selection) => {
- console.log(selection);
- setFlipBehavior(selection);
- setFlipSelectOpen(false);
- }}
- isOpen={flipSelectOpen}
- selections={flipBehavior}
- menuAppendTo={() => document.body}
- >
-
- ['top', 'right', 'bottom', 'left', 'top', 'right', 'bottom']
-
-
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
-
- }
- trigger={trigger.join(' ')}
- enableFlip={enableFlip}
- flipBehavior={flipBehavior === 'flip' ? 'flip' : ['top', 'right', 'bottom', 'left', 'top', 'right', 'bottom']}
- position={position}
- isVisible={isVisible}
- entryDelay={entryDelayInput}
- exitDelay={exitDelayInput}
- animationDuration={animationDuration}
- isContentLeftAligned={contentLeftAligned}
- appendTo={() => document.getElementById('tooltip-boundary')}
- >
- Tooltip
-
-
- >
- );
-};
+```ts file="./TooltipOptions.tsx"
```
diff --git a/packages/react-core/src/components/Tooltip/examples/TooltipBasic.tsx b/packages/react-core/src/components/Tooltip/examples/TooltipBasic.tsx
new file mode 100644
index 00000000000..47b9ef478b2
--- /dev/null
+++ b/packages/react-core/src/components/Tooltip/examples/TooltipBasic.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Button, Tooltip } from '@patternfly/react-core';
+
+export const TooltipBasic: React.FunctionComponent = () => (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ >
+ I have a tooltip!
+
+
+);
diff --git a/packages/react-core/src/components/Tooltip/examples/TooltipIcon.tsx b/packages/react-core/src/components/Tooltip/examples/TooltipIcon.tsx
new file mode 100644
index 00000000000..d2de7a0c2bf
--- /dev/null
+++ b/packages/react-core/src/components/Tooltip/examples/TooltipIcon.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { Tooltip, Button } from '@patternfly/react-core';
+import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
+
+export const TooltipIcon: React.FunctionComponent = () => {
+ const [showSuccessContent, setShowSuccessContent] = React.useState(false);
+ const copyText: string = 'Copy to clipboard';
+ const doneCopyText: string = 'Successfully copied to clipboard!';
+
+ return (
+
+
+ setShowSuccessContent(!showSuccessContent)}
+ >
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Tooltip/examples/TooltipOptions.tsx b/packages/react-core/src/components/Tooltip/examples/TooltipOptions.tsx
new file mode 100644
index 00000000000..c5280ac4f37
--- /dev/null
+++ b/packages/react-core/src/components/Tooltip/examples/TooltipOptions.tsx
@@ -0,0 +1,197 @@
+import React from 'react';
+import { Button, Tooltip, Checkbox, Select, SelectOption, TextInput, TooltipPosition } from '@patternfly/react-core';
+
+export const TooltipOptions: React.FunctionComponent = () => {
+ const [trigger, setTrigger] = React.useState(['mouseenter', 'focus']);
+ const [isVisible, setIsVisible] = React.useState(true);
+ const [contentLeftAligned, setContentLeftAligned] = React.useState(false);
+ const [enableFlip, setEnableFlip] = React.useState(true);
+ const [position, setPosition] = React.useState(TooltipPosition.top);
+ const [positionSelectOpen, setPositionSelectOpen] = React.useState(false);
+ const [flipSelectOpen, setFlipSelectOpen] = React.useState(false);
+ const [flipBehavior, setFlipBehavior] = React.useState('flip');
+ const [entryDelayInput, setEntryDelayInput] = React.useState(0);
+ const [exitDelayInput, setExitDelayInput] = React.useState(0);
+ const [animationDuration, setAnimationDuration] = React.useState(300);
+ const tipBoxRef = React.useRef(null);
+
+ const scrollToRef = (ref: React.RefObject) => {
+ if (ref && ref.current) {
+ ref.current.scrollTop = 400;
+ ref.current.scrollLeft = 300;
+ }
+ };
+
+ React.useEffect(() => {
+ scrollToRef(tipBoxRef);
+ }, []);
+
+ return (
+ <>
+
+
+ {
+ const updatedTrigger = checked ? trigger.concat('mouseenter') : trigger.filter(t => t !== 'mouseenter');
+ setIsVisible(false);
+ setTrigger(updatedTrigger);
+ }}
+ aria-label="trigger: mouseenter"
+ id="trigger_mouseenter"
+ />
+ {
+ const updatedTrigger = checked ? trigger.concat('focus') : trigger.filter(t => t !== 'focus');
+ setIsVisible(false);
+ setTrigger(updatedTrigger);
+ }}
+ aria-label="trigger: focus"
+ id="trigger_focus"
+ />
+ {
+ const updatedTrigger = checked ? trigger.concat('click') : trigger.filter(t => t !== 'click');
+ setIsVisible(false);
+ setTimeout(() => setTrigger(updatedTrigger));
+ }}
+ aria-label="trigger: click"
+ id="trigger_click"
+ />
+ {
+ const updatedTrigger = checked ? trigger.concat('manual') : trigger.filter(t => t !== 'manual');
+ setIsVisible(false);
+ setTrigger(updatedTrigger);
+ }}
+ aria-label="trigger: manual"
+ id="trigger_manual"
+ />
+
+
+ setContentLeftAligned(checked)}
+ aria-label="content left-aligned"
+ id="content_left_aligned"
+ />
+
+
+ setEnableFlip(checked)}
+ aria-label="enableFlip"
+ id="enable_flip"
+ />
+
+
+ position (will flip if enableFlip is true). The 'auto' position requires enableFlip to be set to true.
+ setPositionSelectOpen(!positionSelectOpen)}
+ onSelect={(_event, selection) => {
+ setPosition(selection.toString() as TooltipPosition);
+ setPositionSelectOpen(false);
+ }}
+ isOpen={positionSelectOpen}
+ selections={position}
+ menuAppendTo={() => document.body}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setIsVisible(checked)}
+ aria-label="isVisible"
+ id="is_visible"
+ />
+
+
+ Entry delay (ms){' '}
+ setEntryDelayInput(Number(val))}
+ aria-label="entry delay"
+ />
+ Exit delay (ms){' '}
+ setExitDelayInput(Number(val))}
+ aria-label="exit delay"
+ />
+ Animation duration (ms){' '}
+ setAnimationDuration(Number(val))}
+ aria-label="animation duration"
+ />
+
+
+ flip behavior examples (enableFlip has to be true). "flip" will try to flip the tooltip to the opposite of the
+ starting position. The second option ensures that there are 3 escape positions for every possible starting
+ position (default). This setting is ignored if position prop is set to 'auto'.
+ setFlipSelectOpen(!flipSelectOpen)}
+ onSelect={(_event, selection) => {
+ setFlipBehavior(selection.toString());
+ setFlipSelectOpen(false);
+ }}
+ isOpen={flipSelectOpen}
+ selections={flipBehavior}
+ menuAppendTo={() => document.body}
+ >
+
+ ['top', 'right', 'bottom', 'left', 'top', 'right', 'bottom']
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ trigger={trigger.join(' ')}
+ enableFlip={enableFlip}
+ flipBehavior={flipBehavior === 'flip' ? 'flip' : ['top', 'right', 'bottom', 'left', 'top', 'right', 'bottom']}
+ position={position}
+ isVisible={isVisible}
+ entryDelay={entryDelayInput}
+ exitDelay={exitDelayInput}
+ animationDuration={animationDuration}
+ isContentLeftAligned={contentLeftAligned}
+ appendTo={() => document.getElementById('tooltip-boundary') as HTMLElement}
+ >
+ Tooltip
+
+
+ >
+ );
+};
diff --git a/packages/react-core/src/components/Tooltip/examples/TooltipReactRef.tsx b/packages/react-core/src/components/Tooltip/examples/TooltipReactRef.tsx
new file mode 100644
index 00000000000..d36cb67103a
--- /dev/null
+++ b/packages/react-core/src/components/Tooltip/examples/TooltipReactRef.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { Tooltip } from '@patternfly/react-core';
+
+export const TooltipReactRef: React.FunctionComponent = () => {
+ const tooltipRef = React.useRef(null);
+ return (
+
+
+ Tooltip attached via react ref
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ reference={tooltipRef}
+ />
+
+ );
+};
diff --git a/packages/react-core/src/components/Tooltip/examples/TooltipSelectorRef.tsx b/packages/react-core/src/components/Tooltip/examples/TooltipSelectorRef.tsx
new file mode 100644
index 00000000000..e90b55ae770
--- /dev/null
+++ b/packages/react-core/src/components/Tooltip/examples/TooltipSelectorRef.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Tooltip } from '@patternfly/react-core';
+
+export const TooltipSelectorRef: React.FunctionComponent = () => (
+
+
+ Tooltip attached via selector ref
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id feugiat augue, nec fringilla turpis.
+
+ }
+ reference={() => document.getElementById('tooltip-selector') as HTMLButtonElement}
+ />
+
+);
diff --git a/packages/react-core/src/components/Wizard/examples/FinishedStep.js b/packages/react-core/src/components/Wizard/examples/FinishedStep.js
deleted file mode 100644
index 6adfd128f09..00000000000
--- a/packages/react-core/src/components/Wizard/examples/FinishedStep.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import {
- EmptyState,
- EmptyStateIcon,
- EmptyStateBody,
- EmptyStateSecondaryActions,
- Title,
- Progress,
- Button
-} from '@patternfly/react-core';
-// eslint-disable-next-line patternfly-react/import-tokens-icons
-import { CogsIcon } from '@patternfly/react-icons';
-
-const propTypes = {
- onClose: PropTypes.func.isRequired
-};
-
-class FinishedStep extends React.Component {
- constructor(props) {
- super(props);
- this.state = { percent: 0 };
- }
-
- tick() {
- if (this.state.percent < 100) {
- this.setState(prevState => ({
- percent: prevState.percent + 20
- }));
- }
- }
-
- componentDidMount() {
- this.interval = setInterval(() => this.tick(), 1000);
- }
-
- componentWillUnmount() {
- clearInterval(this.interval);
- }
-
- render() {
- const { percent } = this.state;
- return (
-
-
-
-
- {percent === 100 ? 'Validation complete' : 'Validating credentials'}
-
-
-
-
-
- Description can be used to further elaborate on the validation step, or give the user a better idea of how
- long the process will take.
-
-
-
- Log to console
-
-
-
-
- );
- }
-}
-
-FinishedStep.propTypes = propTypes;
-
-export default FinishedStep;
diff --git a/packages/react-core/src/components/Wizard/examples/SampleForm.js b/packages/react-core/src/components/Wizard/examples/SampleForm.js
deleted file mode 100644
index 9992752f404..00000000000
--- a/packages/react-core/src/components/Wizard/examples/SampleForm.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { Form, FormGroup, TextInput } from '@patternfly/react-core';
-
-const propTypes = {
- formValue: PropTypes.string,
- isFormValid: PropTypes.bool,
- onChange: PropTypes.func
-};
-
-const defaultProps = {
- formValue: '',
- isFormValid: false,
- onChange: () => undefined
-};
-
-class SampleForm extends React.Component {
- static propTypes = propTypes;
- static defaultProps = defaultProps;
-
- state = {
- value: this.props.formValue,
- isValid: this.props.isFormValid
- };
-
- handleTextInputChange = value => {
- const isValid = /^\d+$/.test(value);
- this.setState({ value, isValid });
- this.props.onChange && this.props.onChange(isValid, value);
- };
-
- render() {
- const { value, isValid } = this.state;
- const validated = isValid ? 'default' : 'error';
-
- return (
-
-
-
-
-
- );
- }
-}
-
-export default SampleForm;
diff --git a/packages/react-core/src/components/Wizard/examples/Wizard.md b/packages/react-core/src/components/Wizard/examples/Wizard.md
index 14e290edce3..c614531676e 100644
--- a/packages/react-core/src/components/Wizard/examples/Wizard.md
+++ b/packages/react-core/src/components/Wizard/examples/Wizard.md
@@ -8,11 +8,11 @@ ouia: true
---
import { Button, Drawer, DrawerActions, DrawerCloseButton, DrawerColorVariant,
-DrawerContent, DrawerContentBody, DrawerHead, DrawerPanelContent, DrawerSection, Wizard, WizardFooter, WizardContextConsumer, ModalVariant, Alert, EmptyState, EmptyStateIcon, EmptyStateBody, EmptyStateSecondaryActions, Title, Progress } from '@patternfly/react-core';
+DrawerContent, DrawerContentBody, DrawerHead, DrawerPanelContent, DrawerSection, Wizard, WizardFooter, WizardContextConsumer, ModalVariant, Alert, EmptyState, EmptyStateIcon, EmptyStateBody, EmptyStateSecondaryActions, Title, Progress, Form, FormGroup, TextInput } from '@patternfly/react-core';
import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
import SlackHashIcon from '@patternfly/react-icons/dist/esm/icons/slack-hash-icon';
-import FinishedStep from './FinishedStep';
-import SampleForm from './SampleForm';
+import CogsIcon from '@patternfly/react-icons/dist/esm/icons/cogs-icon';
+
If you seek a wizard solution that allows for more composition, see the [React next](/components/wizard/react-next) tab.
@@ -20,374 +20,37 @@ If you seek a wizard solution that allows for more composition, see the [React n
### Basic
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class SimpleWizard extends React.Component {
- constructor(props) {
- super(props);
- }
-
- render() {
- const steps = [
- { name: 'First step', component: Step 1 content
},
- { name: 'Second step', component: Step 2 content
},
- { name: 'Third step', component: Step 3 content
},
- { name: 'Fourth step', component: Step 4 content
},
- { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
- ];
- const title = 'Basic wizard';
- return ;
- }
-}
+```ts file="./WizardBasic.tsx"
```
### Basic with disabled steps
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class SimpleWizard extends React.Component {
- constructor(props) {
- super(props);
- }
-
- render() {
- const steps = [
- { name: 'First step', component: Step 1 content
},
- { name: 'Second step', component: Step 2 content
, isDisabled: true },
- { name: 'Third step', component: Step 3 content
},
- { name: 'Fourth step', component: Step 4 content
, isDisabled: true },
- { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
- ];
- const title = 'Basic wizard';
- return ;
- }
-}
+```ts file="./WizardBasicWithDisabledSteps.tsx"
```
### Anchors for nav items
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
-import SlackHashIcon from '@patternfly/react-icons/dist/esm/icons/slack-hash-icon';
-
-class WizardWithNavAnchors extends React.Component {
- constructor(props) {
- super(props);
- }
-
- render() {
- const steps = [
- {
- name: (
-
- PF3
-
- ),
- component: Step 1: Read about PF3
,
- stepNavItemProps: { navItemComponent: 'a', href: 'https://www.patternfly.org/v3/', target: '_blank' }
- },
- {
- name: (
-
- PF4
-
- ),
- component: Step 2: Read about PF4
,
- stepNavItemProps: { navItemComponent: 'a', href: 'https://www.patternfly.org/v4/', target: '_blank' }
- },
- {
- name: (
-
- Join us on slack
-
- ),
- component: (
-
- Join the conversation
-
- ),
- stepNavItemProps: { navItemComponent: 'a', href: 'https://patternfly.slack.com/', target: '_blank' }
- }
- ];
- const title = 'Anchor link wizard';
- return ;
- }
-}
+```ts file="./WizardAnchorsForNavItems.tsx"
```
### Incrementally enabled steps
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class IncrementallyEnabledStepsWizard extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- stepIdReached: 1
- };
- this.onNext = ({ id }) => {
- const [, orderIndex] = id.split('-');
-
- this.setState({
- stepIdReached: this.state.stepIdReached < orderIndex ? orderIndex : this.state.stepIdReached
- });
- };
- this.closeWizard = () => {
- console.log('close wizard');
- };
- }
-
- render() {
- const { stepIdReached } = this.state;
-
- const steps = [
- { id: 'incrementallyEnabled-1', name: 'First step', component: Step 1 content
},
- {
- id: 'incrementallyEnabled-2',
- name: 'Second step',
- component: Step 2 content
,
- canJumpTo: stepIdReached >= 2
- },
- {
- id: 'incrementallyEnabled-3',
- name: 'Third step',
- component: Step 3 content
,
- canJumpTo: stepIdReached >= 3
- },
- {
- id: 'incrementallyEnabled-4',
- name: 'Fourth step',
- component: Step 4 content
,
- canJumpTo: stepIdReached >= 4
- },
- {
- id: 'incrementallyEnabled-5',
- name: 'Review',
- component: Review step content
,
- nextButtonText: 'Finish',
- canJumpTo: stepIdReached >= 5
- }
- ];
- const title = 'Incrementally enabled wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardIncrementallyEnabledSteps.tsx"
```
### Expandable steps
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class SimpleWizard extends React.Component {
- constructor(props) {
- super(props);
- }
-
- render() {
- const steps = [
- {
- name: 'First step',
- steps: [
- { name: 'Substep A', component: Substep A content
},
- { name: 'Substep B', component: Substep B content
}
- ]
- },
- { name: 'Second step', component: Step 2 content
},
- {
- name: 'Third step',
- steps: [
- { name: 'Substep C', component: Substep C content
},
- { name: 'Substep D', component: Substep D content
}
- ]
- },
- { name: 'Fourth step', component: Step 4 content
},
- { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
- ];
- const title = 'Expandable wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardExpandableSteps.tsx"
```
### Finished
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-import FinishedStep from './examples/FinishedStep';
-
-class FinishedStepWizard extends React.Component {
- constructor(props) {
- super(props);
-
- this.closeWizard = () => {
- console.log('close wizard');
- };
- }
-
- render() {
- const steps = [
- { name: 'First step', component: Step 1 content
},
- { name: 'Second step', component: Step 2 content
},
- { name: 'Third step', component: Step 3 content
},
- { name: 'Fourth step', component: Step 4 content
},
- { name: 'Review', component: Review step content
, nextButtonText: 'Finish' },
- { name: 'Finish', component: , isFinishedStep: true }
- ];
- const title = 'Finished wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardFinished.tsx"
```
### Enabled on form validation
-```js
-import React from 'react';
-import { Button, Wizard, Form, FormGroup, TextInput } from '@patternfly/react-core';
-import SampleForm from './examples/SampleForm';
-
-class ValidationWizard extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isFormValid: false,
- formValue: 'Thirty',
- allStepsValid: false,
- stepIdReached: 1
- };
-
- this.closeWizard = () => {
- console.log('close wizard');
- };
-
- this.onFormChange = (isValid, value) => {
- this.setState(
- {
- isFormValid: isValid,
- formValue: value
- },
- this.areAllStepsValid
- );
- };
-
- this.areAllStepsValid = () => {
- this.setState({
- allStepsValid: this.state.isFormValid
- });
- };
-
- this.onNext = ({ id, name }, { prevId, prevName }) => {
- console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
- const [, orderIndex] = id.split('-');
-
- this.setState({
- stepIdReached: this.state.stepIdReached < orderIndex ? orderIndex : this.state.stepIdReached
- });
- this.areAllStepsValid();
- };
-
- this.onBack = ({ id, name }, { prevId, prevName }) => {
- console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
- this.areAllStepsValid();
- };
-
- this.onGoToStep = ({ id, name }, { prevId, prevName }) => {
- console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
- };
-
- this.onSave = () => {
- console.log('Saved and closed the wizard');
- this.setState({
- isOpen: false
- });
- };
- }
-
- render() {
- const { isFormValid, formValue, allStepsValid, stepIdReached } = this.state;
-
- const steps = [
- { id: 'validated-1', name: 'Information', component: Step 1 content
},
- {
- name: 'Configuration',
- steps: [
- {
- id: 'validated-2',
- name: 'Substep A with validation',
- component: ,
- enableNext: isFormValid,
- canJumpTo: stepIdReached >= 2
- },
- { id: 'validated-3', name: 'Substep B', component: Substep B
, canJumpTo: stepIdReached >= 3 }
- ]
- },
- {
- id: 'validated-4',
- name: 'Additional',
- component: Step 3 content
,
- enableNext: allStepsValid,
- canJumpTo: stepIdReached >= 4
- },
- {
- id: 'validated-5',
- name: 'Review',
- component: Step 4 content
,
- nextButtonText: 'Close',
- canJumpTo: stepIdReached >= 5
- }
- ];
- const title = 'Enabled on form validation wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardEnabledOnFormValidation.tsx"
```
### Validate on button press
@@ -407,106 +70,7 @@ interface WizardContext {
}
```
-```js
-import React from 'react';
-import { Button, Wizard, WizardFooter, WizardContextConsumer, Alert } from '@patternfly/react-core';
-import SampleForm from './examples/SampleForm';
-import FinishedStep from './examples/FinishedStep';
-
-class ValidateButtonPressWizard extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- stepsValid: 0
- };
-
- this.closeWizard = () => {
- console.log('close wizard');
- };
-
- this.validateLastStep = onNext => {
- const { stepsValid } = this.state;
- if (stepsValid !== 1) {
- this.setState({
- stepsValid: 1
- });
- } else {
- onNext();
- }
- };
- }
-
- render() {
- const { stepsValid } = this.state;
-
- const steps = [
- { name: 'First step', component: Step 1 content
},
- { name: 'Second step', component: Step 2 content
},
- {
- name: 'Final Step',
- component: (
- <>
- {stepsValid === 1 && (
-
- )}
-
- >
- )
- },
- { name: 'Finish', component: , isFinishedStep: true }
- ];
-
- const CustomFooter = (
-
-
- {({ activeStep, goToStepByName, goToStepById, onNext, onBack, onClose }) => {
- if (activeStep.name !== 'Final Step') {
- return (
- <>
-
- Backward
-
-
- Forward
-
-
- Cancel
-
- >
- );
- }
- // Final step buttons
- return (
- <>
- goToStepByName('First step')}>
- Go to Beginning
-
- this.validateLastStep(onNext)}>Validate
- >
- );
- }}
-
-
- );
- const title = 'Validate on button press wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardValidateOnButtonPress.tsx"
```
### Progressive steps
@@ -514,8 +78,6 @@ class ValidateButtonPressWizard extends React.Component {
```js
import React from 'react';
import { Button, Radio, Wizard, WizardFooter, WizardContextConsumer, Alert } from '@patternfly/react-core';
-import SampleForm from './examples/SampleForm';
-import FinishedStep from './examples/FinishedStep';
class ProgressiveWizard extends React.Component {
constructor(props) {
@@ -644,7 +206,6 @@ class ProgressiveWizard extends React.Component {
}
};
}
-
render() {
const {
stepsValid,
@@ -656,7 +217,6 @@ class ProgressiveWizard extends React.Component {
showOptionsStep,
showReviewStep
} = this.state;
-
const getStartedStep = {
name: 'Get started',
component: (
@@ -680,7 +240,6 @@ class ProgressiveWizard extends React.Component {
)
};
-
const createStep = {
name: 'Create options',
component: (
@@ -704,7 +263,6 @@ class ProgressiveWizard extends React.Component {
)
};
-
const updateStep = {
name: 'Update options',
component: (
@@ -728,7 +286,6 @@ class ProgressiveWizard extends React.Component {
)
};
-
const optionsStep = {
name: showCreateStep ? `${createStepRadio} Options` : `${updateStepRadio} Options`,
steps: [
@@ -746,7 +303,6 @@ class ProgressiveWizard extends React.Component {
}
]
};
-
const reviewStep = {
name: 'Review',
component: (
@@ -756,7 +312,6 @@ class ProgressiveWizard extends React.Component {
)
};
-
const steps = [
getStartedStep,
...(showCreateStep ? [createStep] : []),
@@ -764,7 +319,6 @@ class ProgressiveWizard extends React.Component {
...(showOptionsStep ? [optionsStep] : []),
...(showReviewStep ? [reviewStep] : [])
];
-
const CustomFooter = (
@@ -808,248 +362,15 @@ class ProgressiveWizard extends React.Component {
### Get current step
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class GetCurrentStepWizard extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- step: 1
- };
- this.onCurrentStepChanged = ({ id }) => {
- this.setState({
- step: id
- });
- };
- this.closeWizard = () => {
- console.log('close wizard');
- };
- }
-
- render() {
- const steps = [
- { id: 1, name: 'First step', component: Step 1 content
},
- { id: 2, name: 'Second step', component: Step 2 content
},
- { id: 3, name: 'Third step', component: Step 3 content
},
- { id: 4, name: 'Fourth step', component: Step 4 content
},
- { id: 5, name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
- ];
- const title = 'Get current step wizard';
- return (
-
- );
- }
-}
+```ts file="./WizardGetCurrentStep.tsx"
```
### Wizard in modal
-```js
-import React from 'react';
-import { Button, Wizard } from '@patternfly/react-core';
-
-class WizardInModal extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isOpen: false
- };
- this.handleModalToggle = () => {
- this.setState(({ isOpen }) => ({
- isOpen: !isOpen
- }));
- };
- }
-
- render() {
- const { isOpen } = this.state;
-
- const steps = [
- { name: 'First step', component: Step 1 content
},
- { name: 'Second step', component: Step 2 content
},
- { name: 'Third step', component: Step 3 content
},
- { name: 'Fourth step', component: Step 4 content
},
- { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
- ];
- const title = 'Wizard in modal';
- return (
-
-
- Show Modal
-
-
-
- );
- }
-}
+```ts file="./WizardInModal.tsx"
```
### Wizard with drawer
-```js
-import React from 'react';
-import {
- Button,
- DrawerActions,
- DrawerCloseButton,
- DrawerHead,
- DrawerPanelContent,
- Text,
- TextContent,
- Wizard
-} from '@patternfly/react-core';
-
-class WizardWithDrawer extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isOpen: false,
- isDrawerExpanded: false,
- sectionGray: false,
- panelGray: true,
- contentGray: false
- };
-
- this.drawerRef = React.createRef();
-
- this.onExpand = () => {
- this.drawerRef.current && this.drawerRef.current.focus();
- };
-
- this.onOpenClick = () => {
- this.setState({
- isDrawerExpanded: true
- });
- };
-
- this.onCloseClick = () => {
- this.setState({
- isDrawerExpanded: false
- });
- };
- }
-
- render() {
- const { isDrawerExpanded } = this.state;
-
- const panel1Content = (
-
-
-
- drawer-panel-1 content
-
-
-
-
-
-
- );
-
- const panel2Content = (
-
-
-
- drawer-panel-2 content
-
-
-
-
-
-
- );
-
- const panel3Content = (
-
-
-
- drawer-panel-3 content
-
-
-
-
-
-
- );
-
- const drawerToggleButton = (
-
- Open Drawer
-
- );
-
- const steps = [
- {
- name: 'Information',
- component: Information step content
,
- drawerPanelContent: panel1Content,
- drawerToggleButton: drawerToggleButton
- },
- {
- name: 'Configuration',
- steps: [
- {
- name: 'Substep A',
- component: Substep A content
,
- drawerPanelContent: panel2Content,
- drawerToggleButton: drawerToggleButton
- },
- {
- name: 'Substep B',
- component: Substep B content
,
- drawerPanelContent: panel2Content,
- drawerToggleButton: drawerToggleButton
- },
- {
- name: 'Substep C',
- component: Substep C content
,
- drawerPanelContent: panel2Content,
- drawerToggleButton: drawerToggleButton
- }
- ]
- },
- {
- name: 'Additional',
- component: Additional step content
,
- drawerPanelContent: panel3Content,
- drawerToggleButton: drawerToggleButton
- },
- {
- name: 'Review',
- component: Review step content
,
- nextButtonText: 'Finish'
- }
- ];
-
- const title = 'Wizard with drawer';
-
- return (
-
-
-
- );
- }
-}
+```ts file="./WizardWithDrawer.tsx"
```
diff --git a/packages/react-core/src/components/Wizard/examples/WizardAnchorsForNavItems.tsx b/packages/react-core/src/components/Wizard/examples/WizardAnchorsForNavItems.tsx
new file mode 100644
index 00000000000..6c26cd8e937
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardAnchorsForNavItems.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { Button, Wizard } from '@patternfly/react-core';
+import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon';
+import SlackHashIcon from '@patternfly/react-icons/dist/esm/icons/slack-hash-icon';
+
+export const WizardWithNavAnchors: React.FunctionComponent = () => {
+ const steps = [
+ {
+ name: (
+
+ PF3
+
+ ),
+ component: Step 1: Read about PF3
,
+ stepNavItemProps: { navItemComponent: 'a', href: 'https://www.patternfly.org/v3/', target: '_blank' }
+ },
+ {
+ name: (
+
+ PF4
+
+ ),
+ component: Step 2: Read about PF4
,
+ stepNavItemProps: { navItemComponent: 'a', href: 'https://www.patternfly.org/v4/', target: '_blank' }
+ },
+ {
+ name: (
+
+ Join us on slack
+
+ ),
+ component: (
+
+ Join the conversation
+
+ ),
+ stepNavItemProps: { navItemComponent: 'a', href: 'https://patternfly.slack.com/', target: '_blank' }
+ }
+ ];
+ const title = 'Anchor link wizard example';
+ return ;
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardBasic.tsx b/packages/react-core/src/components/Wizard/examples/WizardBasic.tsx
new file mode 100644
index 00000000000..71eca4176f5
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardBasic.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { Wizard } from '@patternfly/react-core';
+
+export const WizardBasic: React.FunctionComponent = () => {
+ const steps = [
+ { name: 'First step', component: Step 1 content
},
+ { name: 'Second step', component: Step 2 content
},
+ { name: 'Third step', component: Step 3 content
},
+ { name: 'Fourth step', component: Step 4 content
},
+ { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
+ ];
+ const title = 'Basic wizard example';
+ return ;
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardBasicWithDisabledSteps.tsx b/packages/react-core/src/components/Wizard/examples/WizardBasicWithDisabledSteps.tsx
new file mode 100644
index 00000000000..46b92e151e9
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardBasicWithDisabledSteps.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { Wizard } from '@patternfly/react-core';
+
+export const WizardBasicWithDisabledSteps: React.FunctionComponent = () => {
+ const steps = [
+ { name: 'First step', component: Step 1 content
},
+ { name: 'Second step', component: Step 2 content
, isDisabled: true },
+ { name: 'Third step', component: Step 3 content
},
+ { name: 'Fourth step', component: Step 4 content
, isDisabled: true },
+ { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
+ ];
+ const title = 'Basic wizard with disabled steps example';
+ return ;
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardEnabledOnFormValidation.tsx b/packages/react-core/src/components/Wizard/examples/WizardEnabledOnFormValidation.tsx
new file mode 100644
index 00000000000..9121779a59e
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardEnabledOnFormValidation.tsx
@@ -0,0 +1,139 @@
+import React from 'react';
+import { Form, FormGroup, TextInput, Wizard, WizardStep } from '@patternfly/react-core';
+interface PrevStepInfo {
+ prevId?: string | number;
+ prevName: React.ReactNode;
+}
+
+interface sampleFormProps {
+ formValue: string;
+ isFormValid: boolean;
+ onChange?: (isValid: boolean, value: string) => void;
+}
+
+const SampleForm: React.FunctionComponent = (props: sampleFormProps) => {
+ const [value, setValue] = React.useState(props.formValue);
+ const [isValid, setIsValid] = React.useState(props.isFormValid);
+
+ const handleTextInputChange = (value: string) => {
+ const valid = /^\d+$/.test(value);
+ setValue(value);
+ setIsValid(valid);
+ props.onChange && props.onChange(valid, value);
+ };
+
+ const validated = isValid ? 'default' : 'error';
+
+ return (
+
+
+
+
+
+ );
+};
+
+export const WizardFormValidation: React.FunctionComponent = () => {
+ const [isFormValid, setIsFormValid] = React.useState(false);
+ const [formValue, setFormValue] = React.useState('Thirty');
+ const [allStepsValid, setAllStepsValid] = React.useState(false);
+ const [stepIdReached, setStepIdReached] = React.useState(1);
+
+ React.useEffect(() => {
+ setAllStepsValid(isFormValid);
+ }, [isFormValid, stepIdReached]);
+
+ const closeWizard = () => {
+ // eslint-disable-next-line no-console
+ console.log('close wizard');
+ };
+
+ const onFormChange = (isValid: boolean, value: string) => {
+ setIsFormValid(isValid);
+ setFormValue(value);
+ };
+
+ const areAllStepsValid = () => {
+ setAllStepsValid(isFormValid);
+ };
+
+ const onNext = ({ id, name }: WizardStep, { prevId, prevName }: PrevStepInfo) => {
+ // eslint-disable-next-line no-console
+ console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
+ if (id) {
+ if (typeof id === 'string') {
+ const [, orderIndex] = id.split('-');
+ id = parseInt(orderIndex);
+ }
+ setStepIdReached(stepIdReached < id ? id : stepIdReached);
+ }
+ };
+
+ const onBack = ({ id, name }: WizardStep, { prevId, prevName }: PrevStepInfo) => {
+ // eslint-disable-next-line no-console
+ console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
+ areAllStepsValid();
+ };
+
+ const onGoToStep = ({ id, name }: WizardStep, { prevId, prevName }: PrevStepInfo) => {
+ // eslint-disable-next-line no-console
+ console.log(`current id: ${id}, current name: ${name}, previous id: ${prevId}, previous name: ${prevName}`);
+ };
+
+ const steps = [
+ { id: 'validated-1', name: 'Information', component: Step 1 content
},
+ {
+ name: 'Configuration',
+ steps: [
+ {
+ id: 'validated-2',
+ name: 'Substep A with validation',
+ component: ,
+ enableNext: isFormValid,
+ canJumpTo: stepIdReached >= 2
+ },
+ { id: 'validated-3', name: 'Substep B', component: Substep B
, canJumpTo: stepIdReached >= 3 }
+ ]
+ },
+ {
+ id: 'validated-4',
+ name: 'Additional',
+ component: Step 3 content
,
+ enableNext: allStepsValid,
+ canJumpTo: stepIdReached >= 4
+ },
+ {
+ id: 'validated-5',
+ name: 'Review',
+ component: Step 4 content
,
+ nextButtonText: 'Finish',
+ canJumpTo: stepIdReached >= 5
+ }
+ ];
+ const title = 'Wizard enabled on form validation example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardExpandableSteps.tsx b/packages/react-core/src/components/Wizard/examples/WizardExpandableSteps.tsx
new file mode 100644
index 00000000000..5400820f6fe
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardExpandableSteps.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Wizard } from '@patternfly/react-core';
+
+export const WizardExpandableSteps: React.FunctionComponent = () => {
+ const steps = [
+ {
+ name: 'First step',
+ steps: [
+ { name: 'Substep A', component: Substep A content
},
+ { name: 'Substep B', component: Substep B content
}
+ ]
+ },
+ { name: 'Second step', component: Step 2 content
},
+ {
+ name: 'Third step',
+ steps: [
+ { name: 'Substep C', component: Substep C content
},
+ { name: 'Substep D', component: Substep D content
}
+ ]
+ },
+ { name: 'Fourth step', component: Step 4 content
},
+ { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
+ ];
+ const title = 'Expandable wizard example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardFinished.tsx b/packages/react-core/src/components/Wizard/examples/WizardFinished.tsx
new file mode 100644
index 00000000000..072d1098f46
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardFinished.tsx
@@ -0,0 +1,90 @@
+import React from 'react';
+import {
+ EmptyState,
+ EmptyStateIcon,
+ EmptyStateBody,
+ EmptyStateSecondaryActions,
+ Title,
+ Progress,
+ Button,
+ Wizard
+} from '@patternfly/react-core';
+// eslint-disable-next-line patternfly-react/import-tokens-icons
+import { CogsIcon } from '@patternfly/react-icons';
+
+interface finishedProps {
+ onClose: () => void;
+}
+
+const FinishedStep: React.FunctionComponent = (props: finishedProps) => {
+ const [percent, setPercent] = React.useState(0);
+
+ const tick = () => {
+ setPercent(prevPercent => {
+ if (prevPercent < 100) {
+ return prevPercent + 20;
+ } else {
+ return prevPercent;
+ }
+ });
+ };
+
+ React.useEffect(() => {
+ const interval = setInterval(() => tick(), 1000);
+
+ if (percent >= 100) {
+ clearInterval(interval);
+ }
+
+ return () => clearInterval(interval);
+ }, [percent]);
+
+ return (
+
+
+
+
+ {percent === 100 ? 'Validation complete' : 'Validating credentials'}
+
+
+
+
+
+ Description can be used to further elaborate on the validation step, or give the user a better idea of how
+ long the process will take.
+
+
+
+ Log to console
+
+
+
+
+ );
+};
+
+export const WizardFinished: React.FunctionComponent = () => {
+ const closeWizard = () => {
+ // eslint-disable-next-line no-console
+ console.log('close wizard');
+ };
+
+ const steps = [
+ { name: 'First step', component: Step 1 content
},
+ { name: 'Second step', component: Step 2 content
},
+ { name: 'Third step', component: Step 3 content
},
+ { name: 'Fourth step', component: Step 4 content
},
+ { name: 'Review', component: Review step content
, nextButtonText: 'Finish' },
+ { name: 'Finish', component: , isFinishedStep: true }
+ ];
+ const title = 'Finished wizard example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardGetCurrentStep.tsx b/packages/react-core/src/components/Wizard/examples/WizardGetCurrentStep.tsx
new file mode 100644
index 00000000000..f41edaa0f34
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardGetCurrentStep.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Wizard, WizardStep } from '@patternfly/react-core';
+
+export const WizardGetCurrentStep: React.FunctionComponent = () => {
+ const onCurrentStepChanged = ({ id }: WizardStep) => {
+ // eslint-disable-next-line no-console
+ console.log(id);
+ };
+ const closeWizard = () => {
+ // eslint-disable-next-line no-console
+ console.log('close wizard');
+ };
+
+ const steps = [
+ { id: 1, name: 'First step', component: Step 1 content
},
+ { id: 2, name: 'Second step', component: Step 2 content
},
+ { id: 3, name: 'Third step', component: Step 3 content
},
+ { id: 4, name: 'Fourth step', component: Step 4 content
},
+ { id: 5, name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
+ ];
+ const title = 'Get current step wizard example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardInModal.tsx b/packages/react-core/src/components/Wizard/examples/WizardInModal.tsx
new file mode 100644
index 00000000000..98695bf6521
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardInModal.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { Button, Wizard } from '@patternfly/react-core';
+
+export const WizardInModal: React.FunctionComponent = () => {
+ const [isOpen, setIsOpen] = React.useState(false);
+ const handleModalToggle = () => {
+ setIsOpen(!isOpen);
+ };
+ const steps = [
+ { name: 'First step', component: Step 1 content
},
+ { name: 'Second step', component: Step 2 content
},
+ { name: 'Third step', component: Step 3 content
},
+ { name: 'Fourth step', component: Step 4 content
},
+ { name: 'Review', component: Review step content
, nextButtonText: 'Finish' }
+ ];
+ const title = 'Wizard in modal example';
+ return (
+
+
+ Show Modal
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardIncrementallyEnabledSteps.tsx b/packages/react-core/src/components/Wizard/examples/WizardIncrementallyEnabledSteps.tsx
new file mode 100644
index 00000000000..f769c9d7455
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardIncrementallyEnabledSteps.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { Wizard, WizardStep } from '@patternfly/react-core';
+
+export const WizardIncrementallyEnabledSteps: React.FunctionComponent = () => {
+ const [stepIdReached, setStepIdReached] = React.useState(1);
+
+ const onNext = ({ id }: WizardStep) => {
+ if (id) {
+ if (typeof id === 'string') {
+ const [, orderIndex] = id.split('-');
+ id = parseInt(orderIndex);
+ }
+ setStepIdReached(stepIdReached < id ? id : stepIdReached);
+ }
+ };
+
+ const closeWizard = () => {
+ // eslint-disable-next-line no-console
+ console.log('close wizard');
+ };
+
+ const steps = [
+ { id: 'incrementallyEnabled-1', name: 'First step', component: Step 1 content
},
+ {
+ id: 'incrementallyEnabled-2',
+ name: 'Second step',
+ component: Step 2 content
,
+ canJumpTo: stepIdReached >= 2
+ },
+ {
+ id: 'incrementallyEnabled-3',
+ name: 'Third step',
+ component: Step 3 content
,
+ canJumpTo: stepIdReached >= 3
+ },
+ {
+ id: 'incrementallyEnabled-4',
+ name: 'Fourth step',
+ component: Step 4 content
,
+ canJumpTo: stepIdReached >= 4
+ },
+ {
+ id: 'incrementallyEnabled-5',
+ name: 'Review',
+ component: Review step content
,
+ nextButtonText: 'Finish',
+ canJumpTo: stepIdReached >= 5
+ }
+ ];
+ const title = 'Incrementally enabled wizard example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardValidateOnButtonPress.tsx b/packages/react-core/src/components/Wizard/examples/WizardValidateOnButtonPress.tsx
new file mode 100644
index 00000000000..d13172d8f61
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardValidateOnButtonPress.tsx
@@ -0,0 +1,204 @@
+import React from 'react';
+import {
+ EmptyState,
+ EmptyStateIcon,
+ EmptyStateBody,
+ EmptyStateSecondaryActions,
+ Title,
+ Form,
+ FormGroup,
+ TextInput,
+ Progress,
+ Button,
+ Wizard,
+ WizardFooter,
+ WizardContextConsumer,
+ Alert
+} from '@patternfly/react-core';
+// eslint-disable-next-line patternfly-react/import-tokens-icons
+import { CogsIcon } from '@patternfly/react-icons';
+
+interface finishedProps {
+ onClose: () => void;
+}
+
+const FinishedStep: React.FunctionComponent = (props: finishedProps) => {
+ const [percent, setPercent] = React.useState(0);
+
+ const tick = () => {
+ setPercent(prevPercent => {
+ if (prevPercent < 100) {
+ return prevPercent + 20;
+ } else {
+ return prevPercent;
+ }
+ });
+ };
+
+ React.useEffect(() => {
+ const interval = setInterval(() => tick(), 1000);
+
+ if (percent >= 100) {
+ clearInterval(interval);
+ }
+
+ return () => clearInterval(interval);
+ }, [percent]);
+
+ return (
+
+
+
+
+ {percent === 100 ? 'Validation complete' : 'Validating credentials'}
+
+
+
+
+
+ Description can be used to further elaborate on the validation step, or give the user a better idea of how
+ long the process will take.
+
+
+
+ Log to console
+
+
+
+
+ );
+};
+
+interface sampleFormProps {
+ formValue: string;
+ isFormValid: boolean;
+ onChange?: (isValid: boolean, value: string) => void;
+}
+
+const SampleForm: React.FunctionComponent = (props: sampleFormProps) => {
+ const [value, setValue] = React.useState(props.formValue);
+ const [isValid, setIsValid] = React.useState(props.isFormValid);
+
+ const handleTextInputChange = (value: string) => {
+ const valid = /^\d+$/.test(value);
+ setValue(value);
+ setIsValid(valid);
+ props.onChange && props.onChange(valid, value);
+ };
+
+ const validated = isValid ? 'default' : 'error';
+
+ return (
+
+
+
+
+
+ );
+};
+
+export const WizardValidateButtonPress: React.FunctionComponent = () => {
+ const [isFormValid, setIsFormValid] = React.useState(false);
+ const [formValue, setFormValue] = React.useState('Validating on button press');
+ const [stepsValid, setStepsValid] = React.useState(0);
+ const [errorText, setErrorText] = React.useState(false);
+
+ const closeWizard = () => {
+ // eslint-disable-next-line no-console
+ console.log('close wizard');
+ };
+
+ const onFormChange = (isValid: boolean, value: string) => {
+ setIsFormValid(isValid);
+ setFormValue(value);
+ };
+
+ const validateLastStep: (onNext: () => void) => void = onNext => {
+ if (stepsValid !== 1 && !isFormValid) {
+ setErrorText(true);
+ } else {
+ setStepsValid(1);
+ setErrorText(false);
+ onNext();
+ }
+ };
+
+ const steps = [
+ { name: 'First step', component: Step 1 content
},
+ { name: 'Second step', component: Step 2 content
},
+ {
+ name: 'Final Step',
+ component: (
+ <>
+ {errorText && (
+
+ )}
+
+ >
+ )
+ },
+ { name: 'Finish', component: , isFinishedStep: true }
+ ];
+
+ const CustomFooter = (
+
+
+ {({ activeStep, goToStepByName, onNext, onBack, onClose }) => {
+ if (activeStep.name !== 'Final Step') {
+ return (
+ <>
+
+ Forward
+
+
+ Backward
+
+
+ Cancel
+
+ >
+ );
+ }
+ // Final step buttons
+ return (
+ <>
+ validateLastStep(onNext)}>Validate
+ goToStepByName('First step')}>Go to Beginning
+ >
+ );
+ }}
+
+
+ );
+
+ const title = 'Validate on button press wizard example';
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Wizard/examples/WizardWithDrawer.tsx b/packages/react-core/src/components/Wizard/examples/WizardWithDrawer.tsx
new file mode 100644
index 00000000000..9a1e134fa9b
--- /dev/null
+++ b/packages/react-core/src/components/Wizard/examples/WizardWithDrawer.tsx
@@ -0,0 +1,126 @@
+import React from 'react';
+import {
+ Button,
+ DrawerActions,
+ DrawerCloseButton,
+ DrawerColorVariant,
+ DrawerHead,
+ DrawerPanelContent,
+ Wizard
+} from '@patternfly/react-core';
+
+export const WizardWithDrawer: React.FunctionComponent = () => {
+ const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false);
+
+ const drawerRef = React.useRef(null);
+
+ const onOpenClick = () => {
+ setIsDrawerExpanded(true);
+ };
+
+ const onCloseClick = () => {
+ setIsDrawerExpanded(false);
+ };
+
+ const panel1Content = (
+
+
+
+ drawer-panel-1 content
+
+
+
+
+
+
+ );
+
+ const panel2Content = (
+
+
+
+ drawer-panel-2 content
+
+
+
+
+
+
+ );
+
+ const panel3Content = (
+
+
+
+ drawer-panel-3 content
+
+
+
+
+
+
+ );
+
+ const drawerToggleButton = (
+
+ Open Drawer
+
+ );
+
+ const steps = [
+ {
+ name: 'Information',
+ component: Information step content
,
+ drawerPanelContent: panel1Content,
+ drawerToggleButton
+ },
+ {
+ name: 'Configuration',
+ steps: [
+ {
+ name: 'Substep A',
+ component: Substep A content
,
+ drawerPanelContent: panel2Content,
+ drawerToggleButton
+ },
+ {
+ name: 'Substep B',
+ component: Substep B content
,
+ drawerPanelContent: panel2Content,
+ drawerToggleButton
+ },
+ {
+ name: 'Substep C',
+ component: Substep C content
,
+ drawerPanelContent: panel2Content,
+ drawerToggleButton
+ }
+ ]
+ },
+ {
+ name: 'Additional',
+ component: Additional step content
,
+ drawerPanelContent: panel3Content,
+ drawerToggleButton
+ },
+ {
+ name: 'Review',
+ component: Review step content
,
+ nextButtonText: 'Finish'
+ }
+ ];
+
+ const title = 'Wizard with drawer example';
+
+ return (
+
+
+
+ );
+};
diff --git a/packages/react-core/src/demos/Card/Card.md b/packages/react-core/src/demos/Card/Card.md
index 7885236dcc2..84996a76734 100644
--- a/packages/react-core/src/demos/Card/Card.md
+++ b/packages/react-core/src/demos/Card/Card.md
@@ -426,11 +426,12 @@ class CardViewBasic extends React.Component {
aria-label={anySelected ? 'Deselect all' : 'Select all'}
isChecked={areAllSelected}
onClick={this.splitCheckboxSelectAll.bind(this)}
- >
+ >
+ {numSelected !== 0 && `${numSelected} selected`}
+
]}
onToggle={this.onSplitButtonToggle}
>
- {numSelected !== 0 && {numSelected} selected }
}
isOpen={splitButtonDropdownIsOpen}
diff --git a/packages/react-core/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx b/packages/react-core/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx
index 6ef23c750fb..91787396069 100644
--- a/packages/react-core/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx
+++ b/packages/react-core/src/demos/ComposableMenu/examples/ComposableApplicationLauncher.tsx
@@ -10,7 +10,7 @@ import {
Popper,
Tooltip,
Divider,
- TextInput
+ SearchInput
} from '@patternfly/react-core';
import { Link } from '@reach/router';
import ThIcon from '@patternfly/react-icons/dist/js/icons/th-icon';
@@ -250,12 +250,7 @@ export const ComposableApplicationLauncher: React.FunctionComponent = () => {
// eslint-disable-next-line no-console
console.log('selected', itemId)}>
- onTextChange(value)}
- />
+ onTextChange(value)} />
diff --git a/packages/react-core/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx b/packages/react-core/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx
index a2def27500d..ac62864084a 100644
--- a/packages/react-core/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx
+++ b/packages/react-core/src/demos/ComposableMenu/examples/ComposableContextSelector.tsx
@@ -9,10 +9,10 @@ import {
MenuInput,
Popper,
Divider,
- TextInput,
InputGroup,
Button,
- ButtonVariant
+ ButtonVariant,
+ SearchInput
} from '@patternfly/react-core';
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
@@ -160,7 +160,7 @@ export const ComposableContextSelector: React.FunctionComponent = () => {
>
-