diff options
125 files changed, 1332 insertions, 1831 deletions
diff --git a/awx/ui_next/package-lock.json b/awx/ui_next/package-lock.json index e9635701b6..049b48ad8d 100644 --- a/awx/ui_next/package-lock.json +++ b/awx/ui_next/package-lock.json @@ -1150,31 +1150,6 @@ "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" }, - "@emotion/babel-utils": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz", - "integrity": "sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==", - "requires": { - "@emotion/hash": "^0.6.6", - "@emotion/memoize": "^0.6.6", - "@emotion/serialize": "^0.9.1", - "convert-source-map": "^1.5.1", - "find-root": "^1.1.0", - "source-map": "^0.7.2" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - } - } - }, - "@emotion/hash": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz", - "integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ==" - }, "@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", @@ -1190,50 +1165,6 @@ } } }, - "@emotion/memoize": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz", - "integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ==" - }, - "@emotion/serialize": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz", - "integrity": "sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==", - "requires": { - "@emotion/hash": "^0.6.6", - "@emotion/memoize": "^0.6.6", - "@emotion/unitless": "^0.6.7", - "@emotion/utils": "^0.8.2" - } - }, - "@emotion/stylis": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz", - "integrity": "sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ==" - }, - "@emotion/unitless": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz", - "integrity": "sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg==" - }, - "@emotion/utils": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz", - "integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw==" - }, - "@fortawesome/fontawesome-common-types": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.28.tgz", - "integrity": "sha512-gtis2/5yLdfI6n0ia0jH7NJs5i/Z/8M/ZbQL6jXQhCthEOe5Cr5NcQPhgTvFxNOtURE03/ZqUcEskdn2M+QaBg==" - }, - "@fortawesome/free-brands-svg-icons": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.0.tgz", - "integrity": "sha512-/6xXiJFCMEQxqxXbL0FPJpwq5Cv6MRrjsbJEmH/t5vOvB4dILDpnY0f7zZSlA8+TG7jwlt12miF/yZpZkykucA==", - "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.28" - } - }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -1704,62 +1635,45 @@ "dev": true }, "@patternfly/patternfly": { - "version": "2.71.6", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-2.71.6.tgz", - "integrity": "sha512-mqqtuCVa+/FbyyK8hSAcfEIwNX73+zbnzHpmC4NrW0kyMzSszPtBqev/ZO79ZxGqZUpLOyUBTVaH7oKn8cL35Q==" + "version": "4.10.31", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.10.31.tgz", + "integrity": "sha512-UxdZ/apWRowXYZ5qPz5LPfXwyB4YGpomrCJPX7c36+Zg8jFpYyVqgVYainL8Yf/GrChtC2LKyoHg7UUTtMtp4A==" }, "@patternfly/react-core": { - "version": "3.158.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-3.158.1.tgz", - "integrity": "sha512-LUknvaIBoo0ftu7OBZhyGn7Cu3IfhaO4nXx17M99OYc76yKBv1jJMmTTUh7OX3QyWH961gH1K7Z3GlJV7v57ZA==", + "version": "4.18.14", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.18.14.tgz", + "integrity": "sha512-aFOBX02ud78eCu7rtbUTr+Rj/J7BertxDssSWFb+uDQrUN268dSH9/tvHcDd3//YZrsCoBbky9ngRa4Jt1fryg==", "requires": { - "@patternfly/react-icons": "^3.15.17", - "@patternfly/react-styles": "^3.7.14", - "@patternfly/react-tokens": "^2.8.14", + "@patternfly/react-icons": "^4.3.6", + "@patternfly/react-styles": "^4.3.6", + "@patternfly/react-tokens": "^4.4.5", "focus-trap": "4.0.2", "react-dropzone": "9.0.0", - "tippy.js": "5.1.2" + "tippy.js": "5.1.2", + "tslib": "^1.11.1" }, "dependencies": { "@patternfly/react-icons": { - "version": "3.15.17", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-3.15.17.tgz", - "integrity": "sha512-Q0JAlxEvSAl5kcMSUMItLiKi9fweO941g5+lS45t3o/Rv4Eg91Ig7AyK1YWw6m1ah+/eHslLfox0Uqw7m7usLg==", - "requires": { - "@fortawesome/free-brands-svg-icons": "^5.8.1" - } - }, - "@patternfly/react-tokens": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-2.8.14.tgz", - "integrity": "sha512-pha0XyZ3ZiXuQoKstuFsiEHARFQKUFsS6WxVuRSEyNbGTToRNJkKR9cW5swzHgXK6Fuw5EA2XFHLuu8osj52KA==" + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.4.0.tgz", + "integrity": "sha512-UKQI5luZ6Bd3SLljl4WNFVhtteUiM2lbKz5qjgBZn3zLOW2Zigv6M1zkgII6rMW9Rxql/UEDZkvNmilf84HW+g==" } } }, "@patternfly/react-icons": { - "version": "3.15.17", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-3.15.17.tgz", - "integrity": "sha512-Q0JAlxEvSAl5kcMSUMItLiKi9fweO941g5+lS45t3o/Rv4Eg91Ig7AyK1YWw6m1ah+/eHslLfox0Uqw7m7usLg==", - "requires": { - "@fortawesome/free-brands-svg-icons": "^5.8.1" - } + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.3.5.tgz", + "integrity": "sha512-+GublxpFXR+y/5zygf9q00/LvIvso8jr0mxZGhVxsKmi2dUu7xAvN+T+5vjS9fiMbXf7WXsSPXST/UTiBIVTdQ==" }, "@patternfly/react-styles": { - "version": "3.7.14", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-3.7.14.tgz", - "integrity": "sha512-NVwbPP9JroulfQgj0LOLWKP4DumArW8RrP1FB1lLOCuw13KkuAcFbLN9MSF8ZBwJ8syxGEdux5mDC3jPjsrQiw==", - "requires": { - "camel-case": "^3.0.0", - "css": "^2.2.3", - "cssstyle": "^0.3.1", - "emotion": "^9.2.9", - "emotion-server": "^9.2.9" - } + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.4.0.tgz", + "integrity": "sha512-0guVqVVvLgDMKAqLM9Vb3T9sjSPBGm9DzTURuZrIz/gx9gKuckSA42OS1aTTtZLXz6ryYoOn7uQJiIhaJu1F0Q==" }, "@patternfly/react-tokens": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-2.8.14.tgz", - "integrity": "sha512-pha0XyZ3ZiXuQoKstuFsiEHARFQKUFsS6WxVuRSEyNbGTToRNJkKR9cW5swzHgXK6Fuw5EA2XFHLuu8osj52KA==" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.5.0.tgz", + "integrity": "sha512-cfxWduAIIFuRnuTuTkColGCoGPmdXy2ousabpGd+Yi3vbwWcWYIRlrLuetK1VMmddnt2PW9PnaLDW6bH3+oagQ==" }, "@sheerun/mutationobserver-shim": { "version": "0.3.3", @@ -2565,11 +2479,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -3180,32 +3089,6 @@ "object.assign": "^4.1.0" } }, - "babel-plugin-emotion": { - "version": "9.2.11", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz", - "integrity": "sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@emotion/babel-utils": "^0.6.4", - "@emotion/hash": "^0.6.2", - "@emotion/memoize": "^0.6.1", - "@emotion/stylis": "^0.7.0", - "babel-plugin-macros": "^2.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "convert-source-map": "^1.5.0", - "find-root": "^1.1.0", - "mkdirp": "^0.5.1", - "source-map": "^0.5.7", - "touch": "^2.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, "babel-plugin-istanbul": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", @@ -3293,7 +3176,7 @@ }, "babel-plugin-syntax-jsx": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" }, "babel-plugin-syntax-object-rest-spread": { @@ -3735,7 +3618,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { "buffer-xor": "^1.0.3", @@ -3769,7 +3652,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { "bn.js": "^4.1.0", @@ -3867,11 +3750,6 @@ } } }, - "buffer-from": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", - "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==" - }, "buffer-indexof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", @@ -3976,15 +3854,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -4633,33 +4502,9 @@ } } }, - "create-emotion": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz", - "integrity": "sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==", - "requires": { - "@emotion/hash": "^0.6.2", - "@emotion/memoize": "^0.6.1", - "@emotion/stylis": "^0.7.0", - "@emotion/unitless": "^0.6.2", - "csstype": "^2.5.2", - "stylis": "^3.5.0", - "stylis-rule-sheet": "^0.0.10" - } - }, - "create-emotion-server": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/create-emotion-server/-/create-emotion-server-9.2.12.tgz", - "integrity": "sha512-ET+E6A5MkQTEBNDYAnjh6+0cB33qStFXhtflkZNPEaOmvzYlB/xcPnpUk4J7ul3MVa8PCQx2Ei5g2MGY/y1n+g==", - "requires": { - "html-tokenize": "^2.0.0", - "multipipe": "^1.0.2", - "through": "^2.3.8" - } - }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { "cipher-base": "^1.0.1", @@ -4671,7 +4516,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { "cipher-base": "^1.0.3", @@ -5015,14 +4860,6 @@ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, - "cssstyle": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz", - "integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==", - "requires": { - "cssom": "0.3.x" - } - }, "csstype": { "version": "2.6.10", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", @@ -5593,7 +5430,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { "bn.js": "^4.1.0", @@ -5792,43 +5629,6 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "requires": { - "readable-stream": "^2.0.2" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -5919,23 +5719,6 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, - "emotion": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.12.tgz", - "integrity": "sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==", - "requires": { - "babel-plugin-emotion": "^9.2.11", - "create-emotion": "^9.2.12" - } - }, - "emotion-server": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/emotion-server/-/emotion-server-9.2.12.tgz", - "integrity": "sha512-Bhjdl7eNoIeiAVa2QPP5d+1nP/31SiO/K1P/qI9cdXCydg91NwGYmteqhhge8u7PF8fLGTEVQfcPwj21815eBw==", - "requires": { - "create-emotion-server": "^9.2.12" - } - }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -6496,7 +6279,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "requires": { "graceful-fs": "^4.1.2", @@ -7145,7 +6928,8 @@ "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true }, "find-up": { "version": "2.1.0", @@ -7220,13 +7004,6 @@ "requires": { "tabbable": "^3.1.2", "xtend": "^4.0.1" - }, - "dependencies": { - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - } } }, "follow-redirects": { @@ -7812,7 +7589,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -7892,18 +7669,6 @@ } } }, - "html-tokenize": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.1.tgz", - "integrity": "sha512-QY6S+hZ0f5m1WT8WffYN+Hg+xm/w5I8XeUcAq/ZYP5wVC8xbKi4Whhru3FtrAebD5EhBW8rmFzkDI6eCAuFe2w==", - "requires": { - "buffer-from": "~0.1.1", - "inherits": "~2.0.1", - "minimist": "~1.2.5", - "readable-stream": "~1.0.27-1", - "through2": "~0.4.1" - } - }, "html-webpack-plugin": { "version": "4.0.0-beta.11", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", @@ -9661,11 +9426,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" - }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -9767,7 +9527,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -10171,15 +9931,6 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, - "multipipe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-1.0.2.tgz", - "integrity": "sha1-zBPv2DPJzamfIk+GhGG44aP9k50=", - "requires": { - "duplexer2": "^0.1.2", - "object-assign": "^4.1.0" - } - }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -10262,17 +10013,9 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "requires": { - "lower-case": "^1.1.1" - } - }, "node-fetch": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", "dev": true, "requires": { @@ -10415,14 +10158,6 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.55.tgz", "integrity": "sha512-H3R3YR/8TjT5WPin/wOoHOUPHgvj8leuU/Keta/rwelEQN9pA/S2Dx8/se4pZ2LBxSd0nAGzsNzhqwa77v7F1w==" }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "requires": { - "abbrev": "1" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -10545,11 +10280,6 @@ "es-abstract": "^1.17.5" } }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, "object-path": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", @@ -10700,7 +10430,7 @@ "dependencies": { "ansi-escapes": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", "dev": true }, @@ -10746,7 +10476,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -10808,7 +10538,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -10829,7 +10559,7 @@ }, "opn": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "resolved": "http://registry.npmjs.org/opn/-/opn-4.0.2.tgz", "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", "dev": true, "requires": { @@ -13284,7 +13014,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "~1.0.0", @@ -13789,7 +13519,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" @@ -14053,7 +13783,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", @@ -14814,7 +14544,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-indent": { @@ -15133,18 +14863,9 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, - "through2": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - } - }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -15237,14 +14958,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "touch": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/touch/-/touch-2.0.2.tgz", - "integrity": "sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==", - "requires": { - "nopt": "~1.0.10" - } - }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -15458,11 +15171,6 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -16629,12 +16337,9 @@ } }, "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "requires": { - "object-keys": "~0.4.0" - } + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.0", diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json index ce52806302..0dfc6ef79e 100644 --- a/awx/ui_next/package.json +++ b/awx/ui_next/package.json @@ -4,10 +4,9 @@ "private": true, "dependencies": { "@lingui/react": "^2.9.1", - "@patternfly/patternfly": "^2.71.6", - "@patternfly/react-core": "^3.158.1", - "@patternfly/react-icons": "^3.15.17", - "@patternfly/react-tokens": "^2.8.14", + "@patternfly/patternfly": "^4.10.31", + "@patternfly/react-core": "4.18.14", + "@patternfly/react-icons": "^4.3.5", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.jsx index 06b6853220..0c443300be 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.jsx @@ -8,6 +8,8 @@ import { InfoCircleIcon, TimesCircleIcon, } from '@patternfly/react-icons'; +import { withI18n } from '@lingui/react'; +import { t } from '@lingui/macro'; import styled from 'styled-components'; const Header = styled.div` @@ -17,11 +19,14 @@ const Header = styled.div` } `; -export default function AlertModal({ +function AlertModal({ + i18n, isOpen = null, title, + label, variant, children, + i18nHash, ...props }) { const variantIcons = { @@ -60,16 +65,19 @@ export default function AlertModal({ const customHeader = ( <Header> {variant ? variantIcons[variant] : null} - <Title size="2xl">{title}</Title> + <Title id="alert-modal-header-label" size="2xl" headingLevel="h2"> + {title} + </Title> </Header> ); return ( <Modal header={customHeader} - isFooterLeftAligned + aria-label={label || i18n._(t`Alert modal`)} + aria-labelledby="alert-modal-header-label" isOpen={Boolean(isOpen)} - isSmall + variant="small" title={title} {...props} > @@ -77,3 +85,5 @@ export default function AlertModal({ </Modal> ); } + +export default withI18n()(AlertModal); diff --git a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx index da8b234e5c..0173e5a378 100644 --- a/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx +++ b/awx/ui_next/src/components/AlertModal/AlertModal.test.jsx @@ -1,11 +1,11 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; import AlertModal from './AlertModal'; describe('AlertModal', () => { test('renders the expected content', () => { - const wrapper = mount( + const wrapper = mountWithContexts( <AlertModal title="Danger!">Are you sure?</AlertModal> ); expect(wrapper).toHaveLength(1); diff --git a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx index bd348998d1..4f49268c0a 100644 --- a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx +++ b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx @@ -43,7 +43,7 @@ class AnsibleSelect extends React.Component { onChange={this.onSelectChange} onBlur={onBlur} aria-label={i18n._(t`Select Input`)} - isValid={isValid} + validated={isValid ? 'default' : 'error'} className={className} isDisabled={isDisabled} > diff --git a/awx/ui_next/src/components/AppContainer/AppContainer.jsx b/awx/ui_next/src/components/AppContainer/AppContainer.jsx index 690d7fae72..7250bb4352 100644 --- a/awx/ui_next/src/components/AppContainer/AppContainer.jsx +++ b/awx/ui_next/src/components/AppContainer/AppContainer.jsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; import { useHistory, useLocation, withRouter } from 'react-router-dom'; -import { global_breakpoint_md } from '@patternfly/react-tokens'; import { Nav, NavList, @@ -41,15 +40,10 @@ function AppContainer({ i18n, navRouteConfig = [], children }) { const [config, setConfig] = useState({}); const [configError, setConfigError] = useState(null); const [isAboutModalOpen, setIsAboutModalOpen] = useState(false); - const [isNavOpen, setIsNavOpen] = useState( - typeof window !== 'undefined' && - window.innerWidth >= parseInt(global_breakpoint_md.value, 10) - ); const handleAboutModalOpen = () => setIsAboutModalOpen(true); const handleAboutModalClose = () => setIsAboutModalOpen(false); const handleConfigErrorClose = () => setConfigError(null); - const handleNavToggle = () => setIsNavOpen(!isNavOpen); const handleLogout = async () => { await RootAPI.logout(); @@ -79,10 +73,9 @@ function AppContainer({ i18n, navRouteConfig = [], children }) { const header = ( <PageHeader showNavToggle - onNavToggle={handleNavToggle} logo={<BrandLogo />} logoProps={{ href: '/' }} - toolbar={ + headerTools={ <PageHeaderToolbar loggedInUser={config?.me} isAboutDisabled={!config?.version} @@ -95,7 +88,6 @@ function AppContainer({ i18n, navRouteConfig = [], children }) { const sidebar = ( <PageSidebar - isNavOpen={isNavOpen} theme="dark" nav={ <Nav aria-label={i18n._(t`Navigation`)} theme="dark"> @@ -116,7 +108,7 @@ function AppContainer({ i18n, navRouteConfig = [], children }) { return ( <> - <Page usecondensed="True" header={header} sidebar={sidebar}> + <Page isManagedSidebar header={header} sidebar={sidebar}> <ConfigProvider value={config}>{children}</ConfigProvider> </Page> <About diff --git a/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx index 229136c23b..8db7305494 100644 --- a/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx +++ b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx @@ -7,9 +7,9 @@ import { DropdownItem, DropdownToggle, DropdownPosition, - Toolbar, - ToolbarGroup, - ToolbarItem, + PageHeaderTools, + PageHeaderToolsGroup, + PageHeaderToolsItem, Tooltip, } from '@patternfly/react-core'; import { QuestionCircleIcon, UserIcon } from '@patternfly/react-icons'; @@ -62,10 +62,10 @@ class PageHeaderToolbar extends Component { } = this.props; return ( - <Toolbar> - <ToolbarGroup> + <PageHeaderTools> + <PageHeaderToolsGroup> <Tooltip position="left" content={<div>{i18n._(t`Info`)}</div>}> - <ToolbarItem> + <PageHeaderToolsItem> <Dropdown isPlain isOpen={isHelpOpen} @@ -93,10 +93,10 @@ class PageHeaderToolbar extends Component { </DropdownItem>, ]} /> - </ToolbarItem> + </PageHeaderToolsItem> </Tooltip> <Tooltip position="left" content={<div>{i18n._(t`User`)}</div>}> - <ToolbarItem> + <PageHeaderToolsItem> <Dropdown id="toolbar-user-dropdown" isPlain @@ -134,10 +134,10 @@ class PageHeaderToolbar extends Component { </DropdownItem>, ]} /> - </ToolbarItem> + </PageHeaderToolsItem> </Tooltip> - </ToolbarGroup> - </Toolbar> + </PageHeaderToolsGroup> + </PageHeaderTools> ); } } diff --git a/awx/ui_next/src/components/AssociateModal/AssociateModal.jsx b/awx/ui_next/src/components/AssociateModal/AssociateModal.jsx index 86aa534f12..e813e1f70b 100644 --- a/awx/ui_next/src/components/AssociateModal/AssociateModal.jsx +++ b/awx/ui_next/src/components/AssociateModal/AssociateModal.jsx @@ -74,9 +74,9 @@ function AssociateModal({ return ( <Fragment> <Modal - isFooterLeftAligned - isLarge + variant="large" title={title} + aria-label={i18n._(t`Association modal`)} isOpen={isModalOpen} onClose={handleClose} actions={[ diff --git a/awx/ui_next/src/components/Background/Background.jsx b/awx/ui_next/src/components/Background/Background.jsx index 7857a1ed54..755393712c 100644 --- a/awx/ui_next/src/components/Background/Background.jsx +++ b/awx/ui_next/src/components/Background/Background.jsx @@ -1,18 +1,10 @@ import React, { Fragment } from 'react'; -import { BackgroundImage, BackgroundImageSrc } from '@patternfly/react-core'; - -const backgroundImageConfig = { - [BackgroundImageSrc.xs]: './images/pfbg_576.jpg', - [BackgroundImageSrc.xs2x]: './images/pfbg_576@2x.jpg', - [BackgroundImageSrc.sm]: './images/pfbg_768.jpg', - [BackgroundImageSrc.sm2x]: './images/pfbg_768@2x.jpg', - [BackgroundImageSrc.lg]: './images/pfbg_2000.jpg', -}; +import { BackgroundImage } from '@patternfly/react-core'; export default ({ children }) => ( <Fragment> - <BackgroundImage src={backgroundImageConfig} /> + <BackgroundImage /> {children} </Fragment> ); diff --git a/awx/ui_next/src/components/Background/Background.test.jsx b/awx/ui_next/src/components/Background/Background.test.jsx index 382bdba66c..4d306f79a5 100644 --- a/awx/ui_next/src/components/Background/Background.test.jsx +++ b/awx/ui_next/src/components/Background/Background.test.jsx @@ -11,7 +11,7 @@ describe('Background', () => { </Background> ); expect(wrapper).toHaveLength(1); - expect(wrapper.find('BackgroundImage')).toHaveLength(1); + expect(wrapper.find('.pf-c-background-image')).toHaveLength(1); expect(wrapper.find('#test')).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx index 670a4d01a3..93a9b3d7f4 100644 --- a/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx +++ b/awx/ui_next/src/components/Breadcrumbs/Breadcrumbs.jsx @@ -30,19 +30,21 @@ const Breadcrumbs = ({ breadcrumbConfig }) => { ); }; -const Crumb = ({ breadcrumbConfig }) => { +const Crumb = ({ breadcrumbConfig, showDivider }) => { const match = useRouteMatch(); const crumb = breadcrumbConfig[match.url]; let crumbElement = ( - <BreadcrumbItem key={match.url}> + <BreadcrumbItem key={match.url} showDivider={showDivider}> <Link to={match.url}>{crumb}</Link> </BreadcrumbItem> ); if (match.isExact) { crumbElement = ( - <BreadcrumbHeading key="breadcrumb-heading">{crumb}</BreadcrumbHeading> + <BreadcrumbHeading key="breadcrumb-heading" showDivider={showDivider}> + {crumb} + </BreadcrumbHeading> ); } @@ -54,7 +56,7 @@ const Crumb = ({ breadcrumbConfig }) => { <Fragment> {crumbElement} <Route path={`${match.url}/:path`}> - <Crumb breadcrumbConfig={breadcrumbConfig} /> + <Crumb breadcrumbConfig={breadcrumbConfig} showDivider /> </Route> </Fragment> ); diff --git a/awx/ui_next/src/components/Card/TabbedCardHeader.js b/awx/ui_next/src/components/Card/TabbedCardHeader.js deleted file mode 100644 index b73ea8d6c8..0000000000 --- a/awx/ui_next/src/components/Card/TabbedCardHeader.js +++ /dev/null @@ -1,13 +0,0 @@ -import styled from 'styled-components'; -import { CardHeader } from '@patternfly/react-core'; - -const TabbedCardHeader = styled(CardHeader)` - --pf-c-card--first-child--PaddingTop: 0; - --pf-c-card--child--PaddingLeft: 0; - --pf-c-card--child--PaddingRight: 0; - --pf-c-card__header--not-last-child--PaddingBottom: 24px; - --pf-c-card__header--not-last-child--PaddingBottom: 0; - display: flex; -`; - -export default TabbedCardHeader; diff --git a/awx/ui_next/src/components/Card/index.js b/awx/ui_next/src/components/Card/index.js index 860e50a051..93de96efca 100644 --- a/awx/ui_next/src/components/Card/index.js +++ b/awx/ui_next/src/components/Card/index.js @@ -1,3 +1,2 @@ -export { default as TabbedCardHeader } from './TabbedCardHeader'; export { default as CardBody } from './CardBody'; export { default as CardActionsRow } from './CardActionsRow'; diff --git a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx b/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx deleted file mode 100644 index 987908c316..0000000000 --- a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { string } from 'prop-types'; -import { Link } from 'react-router-dom'; -import { Button } from '@patternfly/react-core'; -import { TimesIcon } from '@patternfly/react-icons'; -import { withI18n } from '@lingui/react'; -import { t } from '@lingui/macro'; - -function CardCloseButton({ linkTo, i18n, i18nHash, ...props }) { - if (linkTo) { - return ( - <Link - className="pf-c-button pf-m-plain" - aria-label={i18n._(t`Close`)} - title={i18n._(t`Close`)} - to={linkTo} - {...props} - > - <TimesIcon /> - </Link> - ); - } - return ( - <Button variant="plain" aria-label={i18n._(t`Close`)} {...props}> - <TimesIcon /> - </Button> - ); -} -CardCloseButton.propTypes = { - linkTo: string, -}; -CardCloseButton.defaultProps = { - linkTo: null, -}; - -export default withI18n()(CardCloseButton); diff --git a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.test.jsx b/awx/ui_next/src/components/CardCloseButton/CardCloseButton.test.jsx deleted file mode 100644 index 3564971c72..0000000000 --- a/awx/ui_next/src/components/CardCloseButton/CardCloseButton.test.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import CardCloseButton from './CardCloseButton'; - -describe('<CardCloseButton>', () => { - test('should render close button', () => { - const wrapper = mountWithContexts(<CardCloseButton />); - const button = wrapper.find('Button'); - expect(button).toHaveLength(1); - expect(button.prop('variant')).toBe('plain'); - expect(button.prop('aria-label')).toBe('Close'); - expect(wrapper.find('Link')).toHaveLength(0); - }); - - test('should render close link when `linkTo` prop provided', () => { - const wrapper = mountWithContexts(<CardCloseButton linkTo="/foo" />); - expect(wrapper.find('Button')).toHaveLength(0); - const link = wrapper.find('Link'); - expect(link).toHaveLength(1); - expect(link.prop('to')).toEqual('/foo'); - expect(link.prop('aria-label')).toEqual('Close'); - }); -}); diff --git a/awx/ui_next/src/components/CardCloseButton/index.js b/awx/ui_next/src/components/CardCloseButton/index.js deleted file mode 100644 index 5f2d2157be..0000000000 --- a/awx/ui_next/src/components/CardCloseButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CardCloseButton'; diff --git a/awx/ui_next/src/components/ChipGroup/ChipGroup.jsx b/awx/ui_next/src/components/ChipGroup/ChipGroup.jsx index 972efbe5a7..c16ee052fe 100644 --- a/awx/ui_next/src/components/ChipGroup/ChipGroup.jsx +++ b/awx/ui_next/src/components/ChipGroup/ChipGroup.jsx @@ -4,7 +4,7 @@ import { t } from '@lingui/macro'; import { ChipGroup as PFChipGroup } from '@patternfly/react-core'; import { number, shape } from 'prop-types'; -function ChipGroup({ i18n, numChips, totalChips, ...props }) { +function ChipGroup({ i18n, numChips, totalChips, i18nHash, ...props }) { return ( <PFChipGroup {...props} diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx index 2db4da49bd..7fbcd63cfa 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesDetail.jsx @@ -39,7 +39,7 @@ function VariablesDetail({ value, label, rows, fullHeight }) { fullWidth css="grid-column: 1 / -1" > - <Split gutter="sm"> + <Split hasGutter> <SplitItem> <div className="pf-c-form__label"> <span diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx index 72b4e57f17..f39d413fac 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx @@ -35,7 +35,7 @@ function VariablesField({ return ( <div className="pf-c-form__group"> <FieldHeader> - <Split gutter="sm"> + <Split hasGutter> <SplitItem> <label htmlFor={id} className="pf-c-form__label"> <span className="pf-c-form__label-text">{label}</span> diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx index 4b2deab789..5f3886e20b 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx @@ -35,7 +35,7 @@ function VariablesInput(props) { return ( <div className={`pf-c-form__group ${className || ''}`}> - <Split gutter="sm"> + <Split hasGutter> <SplitItem> <label htmlFor={id} className="pf-c-form__label"> {label} diff --git a/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx b/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx index f01c8468ec..58b34e0dcd 100644 --- a/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx +++ b/awx/ui_next/src/components/ContentEmpty/ContentEmpty.jsx @@ -12,7 +12,9 @@ import { CubesIcon } from '@patternfly/react-icons'; const ContentEmpty = ({ i18n, title = '', message = '' }) => ( <EmptyState variant="full"> <EmptyStateIcon icon={CubesIcon} /> - <Title size="lg">{title || i18n._(t`No items found.`)}</Title> + <Title size="lg" headingLevel="h3"> + {title || i18n._(t`No items found.`)} + </Title> <EmptyStateBody>{message}</EmptyStateBody> </EmptyState> ); diff --git a/awx/ui_next/src/components/ContentError/ContentError.jsx b/awx/ui_next/src/components/ContentError/ContentError.jsx index f0d4f80a1d..3c56a2630d 100644 --- a/awx/ui_next/src/components/ContentError/ContentError.jsx +++ b/awx/ui_next/src/components/ContentError/ContentError.jsx @@ -35,7 +35,7 @@ function ContentError({ error, children, isNotFound, i18n }) { ) : ( <EmptyState variant="full"> <EmptyStateIcon icon={ExclamationTriangleIcon} /> - <Title size="lg"> + <Title size="lg" headingLevel="h3"> {is404 ? i18n._(t`Not Found`) : i18n._(t`Something went wrong...`)} </Title> <EmptyStateBody> diff --git a/awx/ui_next/src/components/CredentialChip/CredentialChip.jsx b/awx/ui_next/src/components/CredentialChip/CredentialChip.jsx index 13576fa750..11e00ee7ed 100644 --- a/awx/ui_next/src/components/CredentialChip/CredentialChip.jsx +++ b/awx/ui_next/src/components/CredentialChip/CredentialChip.jsx @@ -6,7 +6,7 @@ import { Chip } from '@patternfly/react-core'; import { Credential } from '../../types'; import { toTitleCase } from '../../util/strings'; -function CredentialChip({ credential, i18n, ...props }) { +function CredentialChip({ credential, i18n, i18nHash, ...props }) { let type; if (credential.cloud) { type = i18n._(t`Cloud`); diff --git a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx index f8b7adfa8a..7db0bae778 100644 --- a/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx +++ b/awx/ui_next/src/components/DataListToolbar/DataListToolbar.jsx @@ -2,30 +2,21 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Checkbox } from '@patternfly/react-core'; -import styled from 'styled-components'; -import { SearchIcon } from '@patternfly/react-icons'; import { - DataToolbar, - DataToolbarContent as _DataToolbarContent, - DataToolbarGroup as _DataToolbarGroup, - DataToolbarItem, - DataToolbarToggleGroup, -} from '@patternfly/react-core/dist/umd/experimental'; + Checkbox, + Toolbar, + ToolbarContent, + ToolbarGroup, + ToolbarItem, + ToolbarToggleGroup, +} from '@patternfly/react-core'; +import { SearchIcon } from '@patternfly/react-icons'; import ExpandCollapse from '../ExpandCollapse'; import Search from '../Search'; import Sort from '../Sort'; import { SearchColumns, SortColumns, QSConfig } from '../../types'; -const DataToolbarContent = styled(_DataToolbarContent)` - --pf-c-data-toolbar__content--PaddingLeft: 24px; - --pf-c-data-toolbar__content--PaddingRight: 8px; -`; -const DataToolbarGroup = styled(_DataToolbarGroup)` - --pf-c-data-toolbar__group--spacer: 24px; -`; - class DataListToolbar extends React.Component { render() { const { @@ -49,26 +40,26 @@ class DataListToolbar extends React.Component { const showExpandCollapse = onCompact && onExpand; return ( - <DataToolbar + <Toolbar id={`${qsConfig.namespace}-list-toolbar`} clearAllFilters={clearAllFilters} collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> {showSelectAll && ( - <DataToolbarGroup> - <DataToolbarItem> + <ToolbarGroup> + <ToolbarItem> <Checkbox isChecked={isAllSelected} onChange={onSelectAll} aria-label={i18n._(t`Select all`)} id="select-all" /> - </DataToolbarItem> - </DataToolbarGroup> + </ToolbarItem> + </ToolbarGroup> )} - <DataToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg"> - <DataToolbarItem> + <ToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="lg"> + <ToolbarItem> <Search qsConfig={qsConfig} columns={searchColumns} @@ -76,31 +67,31 @@ class DataListToolbar extends React.Component { onReplaceSearch={onReplaceSearch} onRemove={onRemove} /> - </DataToolbarItem> - <DataToolbarItem> + </ToolbarItem> + <ToolbarItem> <Sort qsConfig={qsConfig} columns={sortColumns} onSort={onSort} /> - </DataToolbarItem> - </DataToolbarToggleGroup> + </ToolbarItem> + </ToolbarToggleGroup> {showExpandCollapse && ( - <DataToolbarGroup> + <ToolbarGroup> <Fragment> - <DataToolbarItem> + <ToolbarItem> <ExpandCollapse isCompact={isCompact} onCompact={onCompact} onExpand={onExpand} /> - </DataToolbarItem> + </ToolbarItem> </Fragment> - </DataToolbarGroup> + </ToolbarGroup> )} - <DataToolbarGroup> + <ToolbarGroup> {additionalControls.map(control => ( - <DataToolbarItem key={control.key}>{control}</DataToolbarItem> + <ToolbarItem key={control.key}>{control}</ToolbarItem> ))} - </DataToolbarGroup> - </DataToolbarContent> - </DataToolbar> + </ToolbarGroup> + </ToolbarContent> + </Toolbar> ); } } diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx index 898eef3609..cef7bf885a 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx @@ -7,7 +7,7 @@ import { t } from '@lingui/macro'; import { Card as PFCard, CardBody as PFCardBody, - Expandable as PFExpandable, + ExpandableSection as PFExpandable, } from '@patternfly/react-core'; import getErrorMessage from './getErrorMessage'; diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx index 8accea199c..f2d87cc515 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.test.jsx @@ -39,7 +39,7 @@ describe('ErrorDetail', () => { } /> ); - wrapper.find('Expandable').prop('onToggle')(); + wrapper.find('ExpandableSection').prop('onToggle')(); wrapper.update(); }); }); diff --git a/awx/ui_next/src/components/FormField/FormField.jsx b/awx/ui_next/src/components/FormField/FormField.jsx index 096960ef39..0efa21887a 100644 --- a/awx/ui_next/src/components/FormField/FormField.jsx +++ b/awx/ui_next/src/components/FormField/FormField.jsx @@ -29,14 +29,14 @@ function FormField(props) { helperText={helperText} helperTextInvalid={meta.error} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={label} > <FieldTooltip content={tooltip} maxWidth={tooltipMaxWidth} /> <TextArea id={id} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} resizeOrientation="vertical" {...rest} {...field} @@ -51,14 +51,14 @@ function FormField(props) { helperText={helperText} helperTextInvalid={meta.error} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={label} > <FieldTooltip content={tooltip} maxWidth={tooltipMaxWidth} /> <TextInput id={id} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} {...rest} {...field} type={type} diff --git a/awx/ui_next/src/components/FormField/PasswordField.jsx b/awx/ui_next/src/components/FormField/PasswordField.jsx index f65aa6d3c4..7f4954cb5b 100644 --- a/awx/ui_next/src/components/FormField/PasswordField.jsx +++ b/awx/ui_next/src/components/FormField/PasswordField.jsx @@ -14,7 +14,7 @@ function PasswordField(props) { fieldId={id} helperTextInvalid={meta.error} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={label} > <InputGroup> diff --git a/awx/ui_next/src/components/FormField/PasswordInput.jsx b/awx/ui_next/src/components/FormField/PasswordInput.jsx index 993ee9a523..8fa0977f48 100644 --- a/awx/ui_next/src/components/FormField/PasswordInput.jsx +++ b/awx/ui_next/src/components/FormField/PasswordInput.jsx @@ -44,7 +44,7 @@ function PasswordInput(props) { value={field.value === '$encrypted$' ? '' : field.value} isDisabled={isDisabled} isRequired={isRequired} - isValid={isValid} + validated={isValid ? 'default' : 'error'} type={inputType} onChange={(_, event) => { field.onChange(event); diff --git a/awx/ui_next/src/components/HostForm/HostForm.jsx b/awx/ui_next/src/components/HostForm/HostForm.jsx index 8fa1c63bb2..f7bf4d70c3 100644 --- a/awx/ui_next/src/components/HostForm/HostForm.jsx +++ b/awx/ui_next/src/components/HostForm/HostForm.jsx @@ -27,7 +27,9 @@ const InventoryLookupField = withI18n()(({ i18n, host }) => { label={i18n._(t`Inventory`)} isRequired fieldId="inventory-lookup" - isValid={!inventoryMeta.touched || !inventoryMeta.error} + validated={ + !inventoryMeta.touched || !inventoryMeta.error ? 'default' : 'error' + } helperTextInvalid={inventoryMeta.error} > <FieldTooltip diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/OtherPromptsStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/OtherPromptsStep.jsx index 61151e5c2e..02dc8c54b4 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/OtherPromptsStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/OtherPromptsStep.jsx @@ -101,7 +101,7 @@ function JobTypeField({ i18n }) { <FormGroup fieldId="propmt-job-type" label={i18n._(t`Job Type`)} - isValid={isValid} + validated={isValid ? 'default' : 'error'} > <FieldTooltip content={i18n._(t`For job templates, select run to execute the playbook. @@ -132,7 +132,7 @@ function VerbosityField({ i18n }) { return ( <FormGroup fieldId="prompt-verbosity" - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={i18n._(t`Verbosity`)} > <FieldTooltip diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx index 73da31f76f..aaaf0bbbee 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx @@ -96,7 +96,7 @@ function MultipleChoiceField({ question }) { fieldId={id} helperTextInvalid={meta.error} isRequired={question.required} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={question.question_name} > <FieldTooltip content={question.question_description} /> @@ -124,7 +124,7 @@ function MultiSelectField({ question }) { fieldId={id} helperTextInvalid={meta.error} isRequired={question.required} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={question.question_name} > <FieldTooltip content={question.question_description} /> @@ -139,7 +139,7 @@ function MultiSelectField({ question }) { helpers.setValue(field.value.concat(option)); } }} - isExpanded={isOpen} + isOpen={isOpen} selections={field.value} > {question.choices.split('\n').map(opt => ( diff --git a/awx/ui_next/src/components/ListHeader/ListHeader.jsx b/awx/ui_next/src/components/ListHeader/ListHeader.jsx index ebc745174d..f1ac2a5ae8 100644 --- a/awx/ui_next/src/components/ListHeader/ListHeader.jsx +++ b/awx/ui_next/src/components/ListHeader/ListHeader.jsx @@ -2,10 +2,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import styled from 'styled-components'; -import { - DataToolbar, - DataToolbarContent, -} from '@patternfly/react-core/dist/umd/experimental'; +import { Toolbar, ToolbarContent } from '@patternfly/react-core'; import DataListToolbar from '../DataListToolbar'; import { @@ -107,17 +104,17 @@ class ListHeader extends React.Component { return ( <Fragment> {isEmpty ? ( - <DataToolbar + <Toolbar id={`${qsConfig.namespace}-list-toolbar`} clearAllFilters={this.handleRemoveAll} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <EmptyStateControlsWrapper> {emptyStateControls} </EmptyStateControlsWrapper> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ) : ( <Fragment> {renderToolbar({ diff --git a/awx/ui_next/src/components/Lookup/CredentialLookup.jsx b/awx/ui_next/src/components/Lookup/CredentialLookup.jsx index 07c85e453f..bea2317f41 100644 --- a/awx/ui_next/src/components/Lookup/CredentialLookup.jsx +++ b/awx/ui_next/src/components/Lookup/CredentialLookup.jsx @@ -84,7 +84,7 @@ function CredentialLookup({ <FormGroup fieldId="credential" isRequired={required} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={label} helperTextInvalid={helperTextInvalid} > diff --git a/awx/ui_next/src/components/Lookup/Lookup.jsx b/awx/ui_next/src/components/Lookup/Lookup.jsx index 9c0b761bb7..2662e5ab7d 100644 --- a/awx/ui_next/src/components/Lookup/Lookup.jsx +++ b/awx/ui_next/src/components/Lookup/Lookup.jsx @@ -142,9 +142,9 @@ function Lookup(props) { </ChipHolder> </InputGroup> <Modal - isFooterLeftAligned - isLarge + variant="large" title={i18n._(t`Select ${header || i18n._(t`Items`)}`)} + aria-label={i18n._(t`Lookup modal`)} isOpen={isModalOpen} onClose={closeModal} actions={[ diff --git a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.test.jsx b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.test.jsx index dedd7bc7c7..e19ea8170f 100644 --- a/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.test.jsx +++ b/awx/ui_next/src/components/Lookup/MultiCredentialsLookup.test.jsx @@ -81,7 +81,7 @@ describe('<MultiCredentialsLookup />', () => { }); const chip = wrapper.find('CredentialChip'); expect(chip).toHaveLength(5); - const button = chip.at(1).find('ChipButton'); + const button = chip.at(1).find('Chip Button'); await act(async () => { button.invoke('onClick')(); }); diff --git a/awx/ui_next/src/components/Lookup/OrganizationLookup.jsx b/awx/ui_next/src/components/Lookup/OrganizationLookup.jsx index 1290bca495..b8675b134a 100644 --- a/awx/ui_next/src/components/Lookup/OrganizationLookup.jsx +++ b/awx/ui_next/src/components/Lookup/OrganizationLookup.jsx @@ -49,7 +49,7 @@ function OrganizationLookup({ fieldId="organization" helperTextInvalid={helperTextInvalid} isRequired={required} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={i18n._(t`Organization`)} > <Lookup diff --git a/awx/ui_next/src/components/Lookup/ProjectLookup.jsx b/awx/ui_next/src/components/Lookup/ProjectLookup.jsx index 03c6dbbbe3..b05931c151 100644 --- a/awx/ui_next/src/components/Lookup/ProjectLookup.jsx +++ b/awx/ui_next/src/components/Lookup/ProjectLookup.jsx @@ -63,7 +63,7 @@ function ProjectLookup({ fieldId="project" helperTextInvalid={helperTextInvalid} isRequired={required} - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={i18n._(t`Project`)} > {tooltip && <FieldTooltip content={tooltip} />} diff --git a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx index 43a4b11c77..473db38932 100644 --- a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx +++ b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx @@ -50,8 +50,8 @@ function TagMultiSelect({ onChange, value }) { return name; }} selections={selections} - isExpanded={isExpanded} - ariaLabelledBy="tag-select" + isOpen={isExpanded} + aria-labelledby="tag-select" > {renderOptions(options)} </Select> diff --git a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx index 0e19d31c5d..a4c0a2308c 100644 --- a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx +++ b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx @@ -17,7 +17,7 @@ describe('<TagMultiSelect />', () => { it('should not treat empty string as an option', () => { const wrapper = mount(<TagMultiSelect value="" onChange={jest.fn()} />); wrapper.find('input').simulate('focus'); - expect(wrapper.find('Select').prop('isExpanded')).toEqual(true); + expect(wrapper.find('Select').prop('isOpen')).toEqual(true); expect(wrapper.find('SelectOption')).toHaveLength(0); }); diff --git a/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap b/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap index 841f4d2060..87e881c3bd 100644 --- a/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap +++ b/awx/ui_next/src/components/NotificationList/__snapshots__/NotificationListItem.test.jsx.snap @@ -264,7 +264,7 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe className="pf-c-data-list__item-action sc-bwzfXH llKtln" rowid="items-list-item-9000" > - <Component + <Switch aria-label="Toggle notification start" id="notification-9000-started-toggle" isChecked={false} @@ -273,73 +273,43 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe labelOff="Start" onChange={[Function]} > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Toggle notification start", - "id": "notification-9000-started-toggle", - "isChecked": false, - "isDisabled": false, - "label": "Start", - "labelOff": "Start", - "onChange": [Function], - } - } - consumerContext={null} + <label + className="pf-c-switch" + data-ouia-component-id={0} + data-ouia-component-type="PF4/Switch" + data-ouia-safe={true} + htmlFor="notification-9000-started-toggle" > - <Switch + <input aria-label="Toggle notification start" - className="" + aria-labelledby={null} + checked={false} + className="pf-c-switch__input" + disabled={false} id="notification-9000-started-toggle" - isChecked={false} - isDisabled={false} - label="Start" - labelOff="Start" onChange={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } + type="checkbox" + /> + <span + className="pf-c-switch__toggle" + /> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-on" + id={null} > - <label - className="pf-c-switch" - htmlFor="notification-9000-started-toggle" - > - <input - aria-label="Toggle notification start" - aria-labelledby={null} - checked={false} - className="pf-c-switch__input" - disabled={false} - id="notification-9000-started-toggle" - onChange={[Function]} - type="checkbox" - /> - <span - className="pf-c-switch__toggle" - /> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-on" - id={null} - > - Start - </span> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-off" - id={null} - > - Start - </span> - </label> - </Switch> - </ComponentWithOuia> - </Component> - <Component + Start + </span> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-off" + id={null} + > + Start + </span> + </label> + </Switch> + <Switch aria-label="Toggle notification success" id="notification-9000-success-toggle" isChecked={false} @@ -348,73 +318,43 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe labelOff="Success" onChange={[Function]} > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Toggle notification success", - "id": "notification-9000-success-toggle", - "isChecked": false, - "isDisabled": false, - "label": "Success", - "labelOff": "Success", - "onChange": [Function], - } - } - consumerContext={null} + <label + className="pf-c-switch" + data-ouia-component-id={1} + data-ouia-component-type="PF4/Switch" + data-ouia-safe={true} + htmlFor="notification-9000-success-toggle" > - <Switch + <input aria-label="Toggle notification success" - className="" + aria-labelledby={null} + checked={false} + className="pf-c-switch__input" + disabled={false} id="notification-9000-success-toggle" - isChecked={false} - isDisabled={false} - label="Success" - labelOff="Success" onChange={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } + type="checkbox" + /> + <span + className="pf-c-switch__toggle" + /> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-on" + id={null} > - <label - className="pf-c-switch" - htmlFor="notification-9000-success-toggle" - > - <input - aria-label="Toggle notification success" - aria-labelledby={null} - checked={false} - className="pf-c-switch__input" - disabled={false} - id="notification-9000-success-toggle" - onChange={[Function]} - type="checkbox" - /> - <span - className="pf-c-switch__toggle" - /> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-on" - id={null} - > - Success - </span> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-off" - id={null} - > - Success - </span> - </label> - </Switch> - </ComponentWithOuia> - </Component> - <Component + Success + </span> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-off" + id={null} + > + Success + </span> + </label> + </Switch> + <Switch aria-label="Toggle notification failure" id="notification-9000-error-toggle" isChecked={false} @@ -423,72 +363,42 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe labelOff="Failure" onChange={[Function]} > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Toggle notification failure", - "id": "notification-9000-error-toggle", - "isChecked": false, - "isDisabled": false, - "label": "Failure", - "labelOff": "Failure", - "onChange": [Function], - } - } - consumerContext={null} + <label + className="pf-c-switch" + data-ouia-component-id={2} + data-ouia-component-type="PF4/Switch" + data-ouia-safe={true} + htmlFor="notification-9000-error-toggle" > - <Switch + <input aria-label="Toggle notification failure" - className="" + aria-labelledby={null} + checked={false} + className="pf-c-switch__input" + disabled={false} id="notification-9000-error-toggle" - isChecked={false} - isDisabled={false} - label="Failure" - labelOff="Failure" onChange={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } + type="checkbox" + /> + <span + className="pf-c-switch__toggle" + /> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-on" + id={null} > - <label - className="pf-c-switch" - htmlFor="notification-9000-error-toggle" - > - <input - aria-label="Toggle notification failure" - aria-labelledby={null} - checked={false} - className="pf-c-switch__input" - disabled={false} - id="notification-9000-error-toggle" - onChange={[Function]} - type="checkbox" - /> - <span - className="pf-c-switch__toggle" - /> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-on" - id={null} - > - Failure - </span> - <span - aria-hidden="true" - className="pf-c-switch__label pf-m-off" - id={null} - > - Failure - </span> - </label> - </Switch> - </ComponentWithOuia> - </Component> + Failure + </span> + <span + aria-hidden="true" + className="pf-c-switch__label pf-m-off" + id={null} + > + Failure + </span> + </label> + </Switch> </div> </DataListAction> </StyledComponent> diff --git a/awx/ui_next/src/components/OptionsList/OptionsList.jsx b/awx/ui_next/src/components/OptionsList/OptionsList.jsx index ea106acbf8..3ff42f9f71 100644 --- a/awx/ui_next/src/components/OptionsList/OptionsList.jsx +++ b/awx/ui_next/src/components/OptionsList/OptionsList.jsx @@ -18,7 +18,7 @@ import DataListToolbar from '../DataListToolbar'; import { QSConfig, SearchColumns, SortColumns } from '../../types'; const ModalList = styled.div` - .pf-c-data-toolbar__content { + .pf-c-toolbar__content { padding: 0 !important; } `; diff --git a/awx/ui_next/src/components/PaginatedDataList/__snapshots__/ToolbarDeleteButton.test.jsx.snap b/awx/ui_next/src/components/PaginatedDataList/__snapshots__/ToolbarDeleteButton.test.jsx.snap index 4894060b1a..e837c39658 100644 --- a/awx/ui_next/src/components/PaginatedDataList/__snapshots__/ToolbarDeleteButton.test.jsx.snap +++ b/awx/ui_next/src/components/PaginatedDataList/__snapshots__/ToolbarDeleteButton.test.jsx.snap @@ -97,51 +97,27 @@ exports[`<ToolbarDeleteButton /> should render button 1`] = ` zIndex={9999} > <div> - <Component + <Button aria-label="Delete" isDisabled={true} onClick={[Function]} variant="danger" > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Delete", - "children": "Delete", - "isDisabled": true, - "onClick": [Function], - "variant": "danger", - } - } - consumerContext={null} + <button + aria-disabled={null} + aria-label="Delete" + className="pf-c-button pf-m-danger" + data-ouia-component-id={null} + data-ouia-component-type="PF4/Button" + data-ouia-safe={true} + disabled={true} + onClick={[Function]} + tabIndex={null} + type="button" > - <Button - aria-label="Delete" - isDisabled={true} - onClick={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } - variant="danger" - > - <button - aria-disabled={null} - aria-label="Delete" - className="pf-c-button pf-m-danger" - disabled={true} - onClick={[Function]} - tabIndex={null} - type="button" - > - Delete - </button> - </Button> - </ComponentWithOuia> - </Component> + Delete + </button> + </Button> </div> <Portal containerInfo={ diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx index be126a7ecc..c4f88e825a 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx @@ -117,7 +117,7 @@ describe('<ResourceAccessList />', () => { await sleep(0); wrapper.update(); - const button = wrapper.find('ChipButton').at(0); + const button = wrapper.find('Chip Button').at(0); button.prop('onClick')(); wrapper.update(); @@ -136,7 +136,7 @@ describe('<ResourceAccessList />', () => { ); await sleep(0); wrapper.update(); - const button = wrapper.find('ChipButton').at(0); + const button = wrapper.find('Chip Button').at(0); button.prop('onClick')(); wrapper.update(); @@ -155,7 +155,7 @@ describe('<ResourceAccessList />', () => { ); const button = await waitForElement( wrapper, - 'ChipButton', + 'Chip Button', el => el.length === 2 ); button.at(0).prop('onClick')(); @@ -188,7 +188,7 @@ describe('<ResourceAccessList />', () => { ); const button = await waitForElement( wrapper, - 'ChipButton', + 'Chip Button', el => el.length === 2 ); button.at(1).prop('onClick')(); diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx index 764ca642d3..3fe656a3fb 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx @@ -59,10 +59,10 @@ class ResourceAccessListItem extends React.Component { return ( <Chip key={role.id} - isReadOnly={!role.user_capabilities.unattach} onClick={() => { onRoleDelete(role, accessRecord); }} + isReadOnly={!role.user_capabilities.unattach} > {role.name} </Chip> diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap index 64130d46bd..130f418117 100644 --- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap +++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap @@ -17,22 +17,22 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` } username="jane" > - <AlertModal + <WithI18n actions={ Array [ - <Unknown + <Button aria-label="Confirm delete" onClick={[Function]} variant="danger" > Delete - </Unknown>, - <Unknown + </Button>, + <Button onClick={[Function]} variant="secondary" > Cancel - </Unknown>, + </Button>, ] } isOpen={true} @@ -40,359 +40,387 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` title="Remove Team Access" variant="danger" > - <Modal - actions={ - Array [ - <Unknown - aria-label="Confirm delete" - onClick={[Function]} - variant="danger" - > - Delete - </Unknown>, - <Unknown - onClick={[Function]} - variant="secondary" - > - Cancel - </Unknown>, - ] - } - appendTo={ - <body - class="pf-c-backdrop__open" - > - <div> - <div - class="pf-c-backdrop" - > - <div - class="pf-l-bullseye" - > - <div - aria-describedby="pf-modal-0" - aria-label="Remove Team Access" - aria-modal="true" - class="pf-c-modal-box pf-m-sm" - role="dialog" - > - <button - aria-label="Close" - class="pf-c-button pf-m-plain" - type="button" - > - <svg - aria-hidden="true" - fill="currentColor" - height="1em" - role="img" - style="vertical-align: -0.125em;" - viewBox="0 0 352 512" - width="1em" - > - <path - d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" - transform="" - /> - </svg> - </button> - <div - class="pf-c-title" - > - <div - class="AlertModal__Header-l9z1bu-0 hQFWHX" - > - <svg - aria-hidden="true" - class="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" - fill="currentColor" - height="2em" - role="img" - style="vertical-align: -0.25em;" - viewBox="0 0 512 512" - width="2em" - > - <path - d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" - transform="" - /> - </svg> - <h1 - class="pf-c-title pf-m-2xl" - > - Remove Team Access - </h1> - </div> - </div> - <div - class="pf-c-modal-box__body" - id="pf-modal-0" - > - Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. - <br /> - <br /> - If you only want to remove access for this particular user, please remove them from the team. - </div> - <div - class="pf-c-modal-box__footer pf-m-align-left" - > - <button - aria-label="Confirm delete" - class="pf-c-button pf-m-danger" - type="button" - > - Delete - </button> - <button - class="pf-c-button pf-m-secondary" - type="button" - > - Cancel - </button> - </div> - </div> - </div> - </div> - </div> - </body> - } - ariaDescribedById="" - className="" - header={ - <ForwardRef(AlertModal__Header)> - <ForwardRef(AlertModal___StyledExclamationCircleIcon) - size="lg" - /> - <Title - size="2xl" - > - Remove Team Access - </Title> - </ForwardRef(AlertModal__Header)> - } - hideTitle={false} - isFooterLeftAligned={true} - isLarge={false} - isOpen={true} - isSmall={true} - onClose={[Function]} - showClose={true} - title="Remove Team Access" + <I18n + update={true} + withHash={true} > - <Portal - containerInfo={ - <div> - <div - class="pf-c-backdrop" + <AlertModal + actions={ + Array [ + <Button + aria-label="Confirm delete" + onClick={[Function]} + variant="danger" > - <div - class="pf-l-bullseye" - > - <div - aria-describedby="pf-modal-0" - aria-label="Remove Team Access" - aria-modal="true" - class="pf-c-modal-box pf-m-sm" - role="dialog" - > - <button - aria-label="Close" - class="pf-c-button pf-m-plain" - type="button" - > - <svg - aria-hidden="true" - fill="currentColor" - height="1em" - role="img" - style="vertical-align: -0.125em;" - viewBox="0 0 352 512" - width="1em" - > - <path - d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" - transform="" - /> - </svg> - </button> - <div - class="pf-c-title" - > - <div - class="AlertModal__Header-l9z1bu-0 hQFWHX" - > - <svg - aria-hidden="true" - class="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" - fill="currentColor" - height="2em" - role="img" - style="vertical-align: -0.25em;" - viewBox="0 0 512 512" - width="2em" - > - <path - d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" - transform="" - /> - </svg> - <h1 - class="pf-c-title pf-m-2xl" - > - Remove Team Access - </h1> - </div> - </div> - <div - class="pf-c-modal-box__body" - id="pf-modal-0" - > - Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. - <br /> - <br /> - If you only want to remove access for this particular user, please remove them from the team. - </div> - <div - class="pf-c-modal-box__footer pf-m-align-left" - > - <button - aria-label="Confirm delete" - class="pf-c-button pf-m-danger" - type="button" - > - Delete - </button> - <button - class="pf-c-button pf-m-secondary" - type="button" - > - Cancel - </button> - </div> - </div> - </div> - </div> - </div> + Delete + </Button>, + <Button + onClick={[Function]} + variant="secondary" + > + Cancel + </Button>, + ] } + i18n={"/i18n/"} + isOpen={true} + onClose={[Function]} + title="Remove Team Access" + variant="danger" > - <ModalContent + <Modal actions={ Array [ - <Unknown + <Button aria-label="Confirm delete" onClick={[Function]} variant="danger" > Delete - </Unknown>, - <Unknown + </Button>, + <Button onClick={[Function]} variant="secondary" > Cancel - </Unknown>, + </Button>, ] } - ariaDescribedById="" + appendTo={ + <body + class="pf-c-backdrop__open" + > + <div> + <div + class="pf-c-backdrop" + > + <div + class="pf-l-bullseye" + > + <div + aria-describedby="pf-modal-part-2" + aria-label="Alert modal" + aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1" + aria-modal="true" + class="pf-c-modal-box pf-m-sm" + id="pf-modal-part-0" + role="dialog" + > + <button + aria-label="Close" + class="pf-c-button pf-m-plain" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" + > + <svg + aria-hidden="true" + fill="currentColor" + height="1em" + role="img" + style="vertical-align: -0.125em;" + viewBox="0 0 352 512" + width="1em" + > + <path + d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" + transform="" + /> + </svg> + </button> + <header + class="pf-c-modal-box__header" + > + <div + class="AlertModal__Header-l9z1bu-0 hQFWHX" + > + <svg + aria-hidden="true" + class="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" + fill="currentColor" + height="2em" + role="img" + style="vertical-align: -0.25em;" + viewBox="0 0 512 512" + width="2em" + > + <path + d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" + transform="" + /> + </svg> + <h2 + class="pf-c-title pf-m-2xl" + id="alert-modal-header-label" + > + Remove Team Access + </h2> + </div> + </header> + <div + class="pf-c-modal-box__body" + id="pf-modal-part-2" + > + Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. + <br /> + <br /> + If you only want to remove access for this particular user, please remove them from the team. + </div> + <footer + class="pf-c-modal-box__footer" + > + <button + aria-label="Confirm delete" + class="pf-c-button pf-m-danger" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" + > + Delete + </button> + <button + class="pf-c-button pf-m-secondary" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" + > + Cancel + </button> + </footer> + </div> + </div> + </div> + </div> + </body> + } + aria-describedby="" + aria-label="Alert modal" + aria-labelledby="alert-modal-header-label" className="" + hasNoBodyWrapper={false} header={ <ForwardRef(AlertModal__Header)> <ForwardRef(AlertModal___StyledExclamationCircleIcon) size="lg" /> <Title + headingLevel="h2" + id="alert-modal-header-label" size="2xl" > Remove Team Access </Title> </ForwardRef(AlertModal__Header)> } - hideTitle={false} - id="pf-modal-0" - isFooterLeftAligned={true} - isLarge={false} isOpen={true} - isSmall={true} onClose={[Function]} showClose={true} title="Remove Team Access" + variant="small" > - <Backdrop> - <div - className="pf-c-backdrop" - > - <FocusTrap - active={true} - className="pf-l-bullseye" - focusTrapOptions={ - Object { - "clickOutsideDeactivates": true, - } - } - paused={false} - > + <Portal + containerInfo={ + <div> <div - className="pf-l-bullseye" + class="pf-c-backdrop" > - <ModalBox - className="" - id="pf-modal-0" - isLarge={false} - isSmall={true} - style={Object {}} - title="Remove Team Access" + <div + class="pf-l-bullseye" > <div - aria-describedby="pf-modal-0" - aria-label="Remove Team Access" + aria-describedby="pf-modal-part-2" + aria-label="Alert modal" + aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1" aria-modal="true" - className="pf-c-modal-box pf-m-sm" + class="pf-c-modal-box pf-m-sm" + id="pf-modal-part-0" role="dialog" - style={Object {}} > - <ModalBoxCloseButton - onClose={[Function]} + <button + aria-label="Close" + class="pf-c-button pf-m-plain" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" > - <Component - aria-label="Close" - className="" - onClick={[Function]} - variant="plain" + <svg + aria-hidden="true" + fill="currentColor" + height="1em" + role="img" + style="vertical-align: -0.125em;" + viewBox="0 0 352 512" + width="1em" > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Close", - "children": <TimesIcon - color="currentColor" - noVerticalAlign={false} - size="sm" - title={null} - />, - "className": "", - "onClick": [Function], - "variant": "plain", - } - } - consumerContext={null} + <path + d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" + transform="" + /> + </svg> + </button> + <header + class="pf-c-modal-box__header" + > + <div + class="AlertModal__Header-l9z1bu-0 hQFWHX" + > + <svg + aria-hidden="true" + class="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" + fill="currentColor" + height="2em" + role="img" + style="vertical-align: -0.25em;" + viewBox="0 0 512 512" + width="2em" + > + <path + d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" + transform="" + /> + </svg> + <h2 + class="pf-c-title pf-m-2xl" + id="alert-modal-header-label" + > + Remove Team Access + </h2> + </div> + </header> + <div + class="pf-c-modal-box__body" + id="pf-modal-part-2" + > + Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. + <br /> + <br /> + If you only want to remove access for this particular user, please remove them from the team. + </div> + <footer + class="pf-c-modal-box__footer" + > + <button + aria-label="Confirm delete" + class="pf-c-button pf-m-danger" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" + > + Delete + </button> + <button + class="pf-c-button pf-m-secondary" + data-ouia-component-type="PF4/Button" + data-ouia-safe="true" + type="button" + > + Cancel + </button> + </footer> + </div> + </div> + </div> + </div> + } + > + <ModalContent + actions={ + Array [ + <Button + aria-label="Confirm delete" + onClick={[Function]} + variant="danger" + > + Delete + </Button>, + <Button + onClick={[Function]} + variant="secondary" + > + Cancel + </Button>, + ] + } + aria-describedby="" + aria-label="Alert modal" + aria-labelledby="alert-modal-header-label" + boxId="pf-modal-part-0" + className="" + descriptorId="pf-modal-part-2" + hasNoBodyWrapper={false} + header={ + <ForwardRef(AlertModal__Header)> + <ForwardRef(AlertModal___StyledExclamationCircleIcon) + size="lg" + /> + <Title + headingLevel="h2" + id="alert-modal-header-label" + size="2xl" + > + Remove Team Access + </Title> + </ForwardRef(AlertModal__Header)> + } + isOpen={true} + labelId="pf-modal-part-1" + onClose={[Function]} + showClose={true} + title="Remove Team Access" + variant="small" + > + <Backdrop> + <div + className="pf-c-backdrop" + > + <FocusTrap + active={true} + className="pf-l-bullseye" + focusTrapOptions={ + Object { + "clickOutsideDeactivates": true, + } + } + paused={false} + > + <div + className="pf-l-bullseye" + > + <ModalBox + aria-describedby="pf-modal-part-2" + aria-label="Alert modal" + aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1" + className="" + id="pf-modal-part-0" + style={Object {}} + variant="small" + > + <div + aria-describedby="pf-modal-part-2" + aria-label="Alert modal" + aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1" + aria-modal="true" + className="pf-c-modal-box pf-m-sm" + id="pf-modal-part-0" + role="dialog" + style={Object {}} + > + <ModalBoxCloseButton + onClose={[Function]} > <Button aria-label="Close" className="" onClick={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } variant="plain" > <button aria-disabled={null} aria-label="Close" className="pf-c-button pf-m-plain" + data-ouia-component-id={null} + data-ouia-component-type="PF4/Button" + data-ouia-safe={true} disabled={false} onClick={[Function]} tabIndex={null} @@ -402,7 +430,6 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` color="currentColor" noVerticalAlign={false} size="sm" - title={null} > <svg aria-hidden={true} @@ -426,165 +453,145 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` </TimesIcon> </button> </Button> - </ComponentWithOuia> - </Component> - </ModalBoxCloseButton> - <div - className="pf-c-title" - > - <AlertModal__Header> - <StyledComponent - forwardedComponent={ - Object { - "$$typeof": Symbol(react.forward_ref), - "attrs": Array [], - "componentStyle": ComponentStyle { - "componentId": "AlertModal__Header-l9z1bu-0", - "isStatic": false, - "lastClassName": "hQFWHX", - "rules": Array [ - "display:flex;svg{margin-right:16px;}", - ], - }, - "displayName": "AlertModal__Header", - "foldedComponentIds": Array [], - "render": [Function], - "styledComponentId": "AlertModal__Header-l9z1bu-0", - "target": "div", - "toString": [Function], - "warnTooManyClasses": [Function], - "withComponent": [Function], - } - } - forwardedRef={null} - > - <div - className="AlertModal__Header-l9z1bu-0 hQFWHX" + </ModalBoxCloseButton> + <ModalBoxHeader> + <header + className="pf-c-modal-box__header" > - <AlertModal___StyledExclamationCircleIcon - size="lg" - > + <AlertModal__Header> <StyledComponent forwardedComponent={ Object { "$$typeof": Symbol(react.forward_ref), "attrs": Array [], "componentStyle": ComponentStyle { - "componentId": "AlertModal___StyledExclamationCircleIcon-l9z1bu-1", + "componentId": "AlertModal__Header-l9z1bu-0", "isStatic": false, - "lastClassName": "jOhcwt", + "lastClassName": "hQFWHX", "rules": Array [ - "color:var(--pf-global--danger-color--100)", + "display:flex;svg{margin-right:16px;}", ], }, - "displayName": "AlertModal___StyledExclamationCircleIcon", + "displayName": "AlertModal__Header", "foldedComponentIds": Array [], "render": [Function], - "styledComponentId": "AlertModal___StyledExclamationCircleIcon-l9z1bu-1", - "target": [Function], + "styledComponentId": "AlertModal__Header-l9z1bu-0", + "target": "div", "toString": [Function], "warnTooManyClasses": [Function], "withComponent": [Function], } } forwardedRef={null} - size="lg" > - <ExclamationCircleIcon - className="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" - color="currentColor" - noVerticalAlign={false} - size="lg" - title={null} + <div + className="AlertModal__Header-l9z1bu-0 hQFWHX" > - <svg - aria-hidden={true} - aria-labelledby={null} - className="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" - fill="currentColor" - height="2em" - role="img" - style={ - Object { - "verticalAlign": "-0.25em", + <AlertModal___StyledExclamationCircleIcon + size="lg" + > + <StyledComponent + forwardedComponent={ + Object { + "$$typeof": Symbol(react.forward_ref), + "attrs": Array [], + "componentStyle": ComponentStyle { + "componentId": "AlertModal___StyledExclamationCircleIcon-l9z1bu-1", + "isStatic": false, + "lastClassName": "jOhcwt", + "rules": Array [ + "color:var(--pf-global--danger-color--100)", + ], + }, + "displayName": "AlertModal___StyledExclamationCircleIcon", + "foldedComponentIds": Array [], + "render": [Function], + "styledComponentId": "AlertModal___StyledExclamationCircleIcon-l9z1bu-1", + "target": [Function], + "toString": [Function], + "warnTooManyClasses": [Function], + "withComponent": [Function], + } } - } - viewBox="0 0 512 512" - width="2em" + forwardedRef={null} + size="lg" + > + <ExclamationCircleIcon + className="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" + color="currentColor" + noVerticalAlign={false} + size="lg" + > + <svg + aria-hidden={true} + aria-labelledby={null} + className="AlertModal___StyledExclamationCircleIcon-l9z1bu-1 jOhcwt" + fill="currentColor" + height="2em" + role="img" + style={ + Object { + "verticalAlign": "-0.25em", + } + } + viewBox="0 0 512 512" + width="2em" + > + <path + d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" + transform="" + /> + </svg> + </ExclamationCircleIcon> + </StyledComponent> + </AlertModal___StyledExclamationCircleIcon> + <Title + headingLevel="h2" + id="alert-modal-header-label" + size="2xl" > - <path - d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" - transform="" - /> - </svg> - </ExclamationCircleIcon> + <h2 + className="pf-c-title pf-m-2xl" + id="alert-modal-header-label" + > + Remove Team Access + </h2> + </Title> + </div> </StyledComponent> - </AlertModal___StyledExclamationCircleIcon> - <Title - size="2xl" - > - <h1 - className="pf-c-title pf-m-2xl" - > - Remove Team Access - </h1> - </Title> - </div> - </StyledComponent> - </AlertModal__Header> - </div> - <ModalBoxBody - id="pf-modal-0" - > - <div - className="pf-c-modal-box__body" - id="pf-modal-0" - > - Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. - <br /> - <br /> - If you only want to remove access for this particular user, please remove them from the team. - </div> - </ModalBoxBody> - <ModalBoxFooter - isLeftAligned={true} - > - <div - className="pf-c-modal-box__footer pf-m-align-left" - > - <Component - aria-label="Confirm delete" - key="delete" - onClick={[Function]} - variant="danger" + </AlertModal__Header> + </header> + </ModalBoxHeader> + <ModalBoxBody + id="pf-modal-part-2" > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "Confirm delete", - "children": "Delete", - "onClick": [Function], - "variant": "danger", - } - } - consumerContext={null} + <div + className="pf-c-modal-box__body" + id="pf-modal-part-2" + > + Are you sure you want to remove Member access from The Team? Doing so affects all members of the team. + <br /> + <br /> + If you only want to remove access for this particular user, please remove them from the team. + </div> + </ModalBoxBody> + <ModalBoxFooter> + <footer + className="pf-c-modal-box__footer" > <Button aria-label="Confirm delete" + key="delete" onClick={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } variant="danger" > <button aria-disabled={null} aria-label="Confirm delete" className="pf-c-button pf-m-danger" + data-ouia-component-id={null} + data-ouia-component-type="PF4/Button" + data-ouia-safe={true} disabled={false} onClick={[Function]} tabIndex={null} @@ -593,38 +600,18 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` Delete </button> </Button> - </ComponentWithOuia> - </Component> - <Component - key="cancel" - onClick={[Function]} - variant="secondary" - > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "children": "Cancel", - "onClick": [Function], - "variant": "secondary", - } - } - consumerContext={null} - > <Button + key="cancel" onClick={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } variant="secondary" > <button aria-disabled={null} aria-label={null} className="pf-c-button pf-m-secondary" + data-ouia-component-id={null} + data-ouia-component-type="PF4/Button" + data-ouia-safe={true} disabled={false} onClick={[Function]} tabIndex={null} @@ -633,19 +620,19 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = ` Cancel </button> </Button> - </ComponentWithOuia> - </Component> + </footer> + </ModalBoxFooter> </div> - </ModalBoxFooter> + </ModalBox> </div> - </ModalBox> + </FocusTrap> </div> - </FocusTrap> - </div> - </Backdrop> - </ModalContent> - </Portal> - </Modal> - </AlertModal> + </Backdrop> + </ModalContent> + </Portal> + </Modal> + </AlertModal> + </I18n> + </WithI18n> </DeleteRoleConfirmationModal> `; diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap index bfe1711fe8..f26a2377cb 100644 --- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap +++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/ResourceAccessListItem.test.jsx.snap @@ -92,12 +92,17 @@ exports[`<ResourceAccessListItem /> initially renders succesfully 1`] = ` numChips={5} totalChips={1} > - <Unknown + <Chip + className="" + closeBtnAriaLabel="close" + component="div" + isOverflowChip={false} isReadOnly={false} onClick={[Function]} + tooltipPosition="top" > Member - </Unknown> + </Chip> </WithI18n> } /> @@ -151,12 +156,17 @@ exports[`<ResourceAccessListItem /> initially renders succesfully 1`] = ` numChips={5} totalChips={1} > - <Unknown + <Chip + className="" + closeBtnAriaLabel="close" + component="div" + isOverflowChip={false} isReadOnly={false} onClick={[Function]} + tooltipPosition="top" > Member - </Unknown> + </Chip> </WithI18n> } /> @@ -233,12 +243,17 @@ exports[`<ResourceAccessListItem /> initially renders succesfully 1`] = ` numChips={5} totalChips={1} > - <Unknown + <Chip + className="" + closeBtnAriaLabel="close" + component="div" + isOverflowChip={false} isReadOnly={false} onClick={[Function]} + tooltipPosition="top" > Member - </Unknown> + </Chip> </WithI18n> } /> @@ -661,12 +676,17 @@ exports[`<ResourceAccessListItem /> initially renders succesfully 1`] = ` numChips={5} totalChips={1} > - <Unknown + <Chip + className="" + closeBtnAriaLabel="close" + component="div" + isOverflowChip={false} isReadOnly={false} onClick={[Function]} + tooltipPosition="top" > Member - </Unknown> + </Chip> </WithI18n> } > @@ -801,175 +821,113 @@ exports[`<ResourceAccessListItem /> initially renders succesfully 1`] = ` totalChips={1} > <ChipGroup - className="" + aria-label="Chip group category" + categoryName="" + closeBtnAriaLabel="Close chip group" collapsedText="-4 more" defaultIsOpen={false} expandedText="Show less" + isClosable={false} numChips={5} - withToolbar={false} + onClick={[Function]} + tooltipPosition="top" > - <ul - className="pf-c-chip-group" + <GenerateId + prefix="pf-random-id-" > - <InnerChipGroup - className="" - collapsedText="-4 more" - defaultIsOpen={false} - expandedText="Show less" - isOpen={false} - numChips={5} - onToggleCollapse={[Function]} - withToolbar={false} + <div + className="pf-c-chip-group" > - <Component - component="li" - isReadOnly={false} - key=".$3" - onClick={[Function]} + <ul + aria-label="Chip group category" + className="pf-c-chip-group__list" + role="list" > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "children": "Member", - "component": "li", - "isReadOnly": false, - "onClick": [Function], - } - } - consumerContext={null} + <li + className="pf-c-chip-group__list-item" + key="0" > <Chip className="" closeBtnAriaLabel="close" - component="li" + component="div" isOverflowChip={false} isReadOnly={false} + key=".$3" onClick={[Function]} - ouiaContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } tooltipPosition="top" > <GenerateId prefix="pf-random-id-" > - <li + <div className="pf-c-chip" + data-ouia-component-id={1} + data-ouia-component-type="PF4/Chip" + data-ouia-safe={true} > <span className="pf-c-chip__text" - id="pf-random-id-0" + id="pf-random-id-1" > Member </span> - <ChipButton - aria-labelledby="remove_pf-random-id-0 pf-random-id-0" - ariaLabel="close" - id="remove_pf-random-id-0" + <Button + aria-label="close" + aria-labelledby="remove_pf-random-id-1 pf-random-id-1" + id="remove_pf-random-id-1" onClick={[Function]} + variant="plain" > - <Component + <button + aria-disabled={null} aria-label="close" - aria-labelledby="remove_pf-random-id-0 pf-random-id-0" - className="" - id="remove_pf-random-id-0" + aria-labelledby="remove_pf-random-id-1 pf-random-id-1" + className="pf-c-button pf-m-plain" + data-ouia-component-id={null} + data-ouia-component-type="PF4/Button" + data-ouia-safe={true} + disabled={false} + id="remove_pf-random-id-1" onClick={[Function]} - variant="plain" + tabIndex={null} + type="button" > - <ComponentWithOuia - component={[Function]} - componentProps={ - Object { - "aria-label": "close", - "aria-labelledby": "remove_pf-random-id-0 pf-random-id-0", - "children": <TimesCircleIcon - aria-hidden="true" - color="currentColor" - noVerticalAlign={false} - size="sm" - title={null} - />, - "className": "", - "id": "remove_pf-random-id-0", - "onClick": [Function], - "variant": "plain", - } - } - consumerContext={ - Object { - "isOuia": false, - "ouiaId": null, - } - } + <TimesIcon + aria-hidden="true" + color="currentColor" + noVerticalAlign={false} + size="sm" > - <Button - aria-label="close" - aria-labelledby="remove_pf-random-id-0 pf-random-id-0" - className="" - id="remove_pf-random-id-0" - onClick={[Function]} - ouiaContext={ + <svg + aria-hidden="true" + aria-labelledby={null} + fill="currentColor" + height="1em" + role="img" + style={ Object { - "isOuia": false, - "ouiaId": null, + "verticalAlign": "-0.125em", } } - variant="plain" + viewBox="0 0 352 512" + width="1em" > - <button - aria-disabled={null} - aria-label="close" - aria-labelledby="remove_pf-random-id-0 pf-random-id-0" - className="pf-c-button pf-m-plain" - disabled={false} - id="remove_pf-random-id-0" - onClick={[Function]} - tabIndex={null} - type="button" - > - <TimesCircleIcon - aria-hidden="true" - color="currentColor" - noVerticalAlign={false} - size="sm" - title={null} - > - <svg - aria-hidden="true" - aria-labelledby={null} - fill="currentColor" - height="1em" - role="img" - style={ - Object { - "verticalAlign": "-0.125em", - } - } - viewBox="0 0 512 512" - width="1em" - > - <path - d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z" - transform="" - /> - </svg> - </TimesCircleIcon> - </button> - </Button> - </ComponentWithOuia> - </Component> - </ChipButton> - </li> + <path + d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" + transform="" + /> + </svg> + </TimesIcon> + </button> + </Button> + </div> </GenerateId> </Chip> - </ComponentWithOuia> - </Component> - </InnerChipGroup> - </ul> + </li> + </ul> + </div> + </GenerateId> </ChipGroup> </ChipGroup> </I18n> diff --git a/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx b/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx index a41ac3ea0a..700077e88c 100644 --- a/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx +++ b/awx/ui_next/src/components/RoutedTabs/RoutedTabs.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { shape, string, number, arrayOf, node, oneOfType } from 'prop-types'; -import { Tab, Tabs } from '@patternfly/react-core'; +import { Tab, Tabs, TabTitleText } from '@patternfly/react-core'; import { useHistory } from 'react-router-dom'; function RoutedTabs(props) { @@ -36,7 +36,7 @@ function RoutedTabs(props) { eventKey={tab.id} key={tab.id} link={tab.link} - title={tab.name} + title={<TabTitleText>{tab.name}</TabTitleText>} /> ))} </Tabs> diff --git a/awx/ui_next/src/components/Schedule/Schedule.jsx b/awx/ui_next/src/components/Schedule/Schedule.jsx index 260655f27c..ffa28dd35f 100644 --- a/awx/ui_next/src/components/Schedule/Schedule.jsx +++ b/awx/ui_next/src/components/Schedule/Schedule.jsx @@ -10,13 +10,10 @@ import { useLocation, useParams, } from 'react-router-dom'; -import { CardActions } from '@patternfly/react-core'; import { CaretLeftIcon } from '@patternfly/react-icons'; -import CardCloseButton from '../CardCloseButton'; import RoutedTabs from '../RoutedTabs'; import ContentError from '../ContentError'; import ContentLoading from '../ContentLoading'; -import { TabbedCardHeader } from '../Card'; import ScheduleDetail from './ScheduleDetail'; import ScheduleEdit from './ScheduleEdit'; import { SchedulesAPI } from '../../api'; @@ -90,23 +87,17 @@ function Schedule({ i18n, setBreadcrumb, unifiedJobTemplate }) { return <ContentError error={contentError} />; } - let cardHeader = null; + let showCardHeader = true; + if ( - location.pathname.includes('schedules/') && - !location.pathname.endsWith('edit') + !location.pathname.includes('schedules/') || + location.pathname.endsWith('edit') ) { - cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo={`${pathRoot}schedules`} /> - </CardActions> - </TabbedCardHeader> - ); + showCardHeader = false; } return ( <> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from={`${pathRoot}schedules/:scheduleId`} diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx index 354a283b09..64b8863fe6 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx @@ -24,6 +24,7 @@ import ChipGroup from '../../ChipGroup'; const PromptTitle = styled(Title)` --pf-c-title--m-md--FontWeight: 700; + grid-column: 1 / -1; `; function ScheduleDetail({ schedule, i18n }) { @@ -140,7 +141,7 @@ function ScheduleDetail({ schedule, i18n }) { /> {showPromptedFields && ( <> - <PromptTitle size="md" css="grid-column: 1 / -1;"> + <PromptTitle headingLevel="h2"> {i18n._(t`Prompted Fields`)} </PromptTitle> <Detail label={i18n._(t`Job Type`)} value={job_type} /> diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx index a1739e8c3a..fe9175c6de 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx @@ -136,7 +136,7 @@ describe('<ScheduleDetail />', () => { expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0); }); test('details should render with the proper values with prompts', async () => { - SchedulesAPI.readCredentials.mockResolvedValueOnce({ + SchedulesAPI.readCredentials.mockResolvedValue({ data: { count: 2, results: [ @@ -182,6 +182,7 @@ describe('<ScheduleDetail />', () => { ); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + // await waitForElement(wrapper, 'Title', el => el.length > 0); expect( wrapper .find('Detail[label="Name"]') diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx index fa7627fddd..5203c149c7 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx @@ -36,7 +36,7 @@ function ScheduleOccurrences({ preview = { local: [], utc: [] }, i18n }) { fullWidth css="grid-column: 1 / -1" > - <Split gutter="sm"> + <Split hasGutter> <SplitItem> <OccurrencesLabel> <span>{i18n._(t`Occurrences`)}</span> diff --git a/awx/ui_next/src/components/Schedule/shared/FrequencyDetailSubform.jsx b/awx/ui_next/src/components/Schedule/shared/FrequencyDetailSubform.jsx index d40976e26a..8e6ea219c0 100644 --- a/awx/ui_next/src/components/Schedule/shared/FrequencyDetailSubform.jsx +++ b/awx/ui_next/src/components/Schedule/shared/FrequencyDetailSubform.jsx @@ -231,7 +231,9 @@ const FrequencyDetailSubform = ({ i18n }) => { fieldId="schedule-run-every" helperTextInvalid={intervalMeta.error} isRequired - isValid={!intervalMeta.touched || !intervalMeta.error} + validated={ + !intervalMeta.touched || !intervalMeta.error ? 'default' : 'error' + } label={i18n._(t`Run every`)} > <div css="display: flex"> @@ -255,7 +257,11 @@ const FrequencyDetailSubform = ({ i18n }) => { fieldId="schedule-days-of-week" helperTextInvalid={daysOfWeekMeta.error} isRequired - isValid={!daysOfWeekMeta.touched || !daysOfWeekMeta.error} + validated={ + !daysOfWeekMeta.touched || !daysOfWeekMeta.error + ? 'default' + : 'error' + } label={i18n._(t`On days`)} > <div css="display: flex"> @@ -339,7 +345,9 @@ const FrequencyDetailSubform = ({ i18n }) => { fieldId="schedule-run-on" helperTextInvalid={runOnMeta.error} isRequired - isValid={!runOnMeta.touched || !runOnMeta.error} + validated={ + !runOnMeta.touched || !runOnMeta.error ? 'default' : 'error' + } label={i18n._(t`Run on`)} > <RunOnRadio @@ -502,7 +510,7 @@ const FrequencyDetailSubform = ({ i18n }) => { fieldId="schedule-end" helperTextInvalid={endMeta.error} isRequired - isValid={!endMeta.touched || !endMeta.error} + validated={!endMeta.touched || !endMeta.error ? 'default' : 'error'} label={i18n._(t`End`)} > <Radio @@ -556,7 +564,11 @@ const FrequencyDetailSubform = ({ i18n }) => { fieldId="schedule-end-datetime" helperTextInvalid={endDateTimeMeta.error} isRequired - isValid={!endDateTimeMeta.touched || !endDateTimeMeta.error} + validated={ + !endDateTimeMeta.touched || !endDateTimeMeta.error + ? 'default' + : 'error' + } label={i18n._(t`End date/time`)} > <input diff --git a/awx/ui_next/src/components/Schedule/shared/ScheduleForm.jsx b/awx/ui_next/src/components/Schedule/shared/ScheduleForm.jsx index 795af0035a..756e400258 100644 --- a/awx/ui_next/src/components/Schedule/shared/ScheduleForm.jsx +++ b/awx/ui_next/src/components/Schedule/shared/ScheduleForm.jsx @@ -104,7 +104,11 @@ function ScheduleFormFields({ i18n, zoneOptions }) { fieldId="schedule-start-datetime" helperTextInvalid={startDateTimeMeta.error} isRequired - isValid={!startDateTimeMeta.touched || !startDateTimeMeta.error} + validated={ + !startDateTimeMeta.touched || !startDateTimeMeta.error + ? 'default' + : 'error' + } label={i18n._(t`Start date/time`)} > <input @@ -120,7 +124,9 @@ function ScheduleFormFields({ i18n, zoneOptions }) { fieldId="schedule-timezone" helperTextInvalid={timezoneMeta.error} isRequired - isValid={!timezoneMeta.touched || !timezoneMeta.error} + validated={ + !timezoneMeta.touched || !timezoneMeta.error ? 'default' : 'error' + } label={i18n._(t`Local time zone`)} > <AnsibleSelect @@ -134,7 +140,9 @@ function ScheduleFormFields({ i18n, zoneOptions }) { fieldId="schedule-requency" helperTextInvalid={frequencyMeta.error} isRequired - isValid={!frequencyMeta.touched || !frequencyMeta.error} + validated={ + !frequencyMeta.touched || !frequencyMeta.error ? 'default' : 'error' + } label={i18n._(t`Run frequency`)} > <AnsibleSelect @@ -153,7 +161,9 @@ function ScheduleFormFields({ i18n, zoneOptions }) { </FormGroup> {frequency.value !== 'none' && ( <SubFormLayout> - <Title size="md">{i18n._(t`Frequency Details`)}</Title> + <Title size="md" headingLevel="h4"> + {i18n._(t`Frequency Details`)} + </Title> <FormColumnLayout> <FrequencyDetailSubform /> </FormColumnLayout> diff --git a/awx/ui_next/src/components/Search/Search.jsx b/awx/ui_next/src/components/Search/Search.jsx index 91c3987cea..0fb59e0806 100644 --- a/awx/ui_next/src/components/Search/Search.jsx +++ b/awx/ui_next/src/components/Search/Search.jsx @@ -16,12 +16,10 @@ import { SelectOption, SelectVariant, TextInput, + ToolbarGroup, + ToolbarItem, + ToolbarFilter, } from '@patternfly/react-core'; -import { - DataToolbarGroup, - DataToolbarItem, - DataToolbarFilter, -} from '@patternfly/react-core/dist/umd/experimental'; import { SearchIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; import { parseQueryString } from '../../util/qs'; @@ -205,8 +203,8 @@ class Search extends React.Component { const chipsByKey = getChipsByKey(); return ( - <DataToolbarGroup variant="filter-group"> - <DataToolbarItem> + <ToolbarGroup variant="filter-group"> + <ToolbarItem> {searchDropdownItems.length > 0 ? ( <Dropdown onToggle={this.handleDropdownToggle} @@ -227,10 +225,10 @@ class Search extends React.Component { ) : ( <NoOptionDropdown>{searchColumnName}</NoOptionDropdown> )} - </DataToolbarItem> + </ToolbarItem> {columns.map( ({ key, name, options, isBoolean, booleanLabels = {} }) => ( - <DataToolbarFilter + <ToolbarFilter chips={chipsByKey[key] ? chipsByKey[key].chips : []} deleteChip={(unusedKey, chip) => { const [columnKey, ...value] = chip.key.split(':'); @@ -253,7 +251,7 @@ class Search extends React.Component { const [, ...value] = chip.key.split(':'); return value.join(':'); })} - isExpanded={isFilterDropdownOpen} + isOpen={isFilterDropdownOpen} placeholderText={`Filter By ${name}`} > {options.map(([optionKey, optionLabel]) => ( @@ -272,7 +270,7 @@ class Search extends React.Component { this.handleFilterBooleanSelect(key, selection) } selections={chipsByKey[key].chips[0]} - isExpanded={isFilterDropdownOpen} + isOpen={isFilterDropdownOpen} placeholderText={`Filter By ${name}`} > <SelectOption key="true" value="true"> @@ -311,10 +309,10 @@ class Search extends React.Component { </div> </InputGroup> )} - </DataToolbarFilter> + </ToolbarFilter> ) )} - </DataToolbarGroup> + </ToolbarGroup> ); } } diff --git a/awx/ui_next/src/components/Search/Search.test.jsx b/awx/ui_next/src/components/Search/Search.test.jsx index 503fa9ec58..5eac1c532b 100644 --- a/awx/ui_next/src/components/Search/Search.test.jsx +++ b/awx/ui_next/src/components/Search/Search.test.jsx @@ -1,8 +1,5 @@ import React from 'react'; -import { - DataToolbar, - DataToolbarContent, -} from '@patternfly/react-core/dist/umd/experimental'; +import { Toolbar, ToolbarContent } from '@patternfly/react-core'; import { createMemoryHistory } from 'history'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; @@ -33,15 +30,15 @@ describe('<Search />', () => { const onSearch = jest.fn(); search = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} /> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ); search.find(searchTextInput).instance().value = 'test-321'; @@ -56,15 +53,15 @@ describe('<Search />', () => { const columns = [{ name: 'Name', key: 'name', isDefault: true }]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} /> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ).find('Search'); expect(wrapper.state('isSearchDropdownOpen')).toEqual(false); wrapper.instance().handleDropdownToggle(true); @@ -78,15 +75,15 @@ describe('<Search />', () => { ]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} /> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ).find('Search'); expect(wrapper.state('searchKey')).toEqual('name'); wrapper @@ -101,15 +98,15 @@ describe('<Search />', () => { const columns = [{ name: 'Name', key: 'name', isDefault: true }]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} /> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ); wrapper.find(searchTextInput).instance().value = ''; @@ -125,15 +122,15 @@ describe('<Search />', () => { const columns = [{ name: 'Name', key: 'name', isDefault: true }]; const onSearch = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} onSearch={onSearch} /> - </DataToolbarContent> - </DataToolbar> + </ToolbarContent> + </Toolbar> ); wrapper.find(searchTextInput).instance().value = 'test-321'; @@ -156,23 +153,23 @@ describe('<Search />', () => { initialEntries: [`/organizations/${query}`], }); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${QS_CONFIG.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={QS_CONFIG} columns={columns} /> - </DataToolbarContent> - </DataToolbar>, + </ToolbarContent> + </Toolbar>, { context: { router: { history } } } ); const typeFilterWrapper = wrapper.find( - 'DataToolbarFilter[categoryName="Type"]' + 'ToolbarFilter[categoryName="Type"]' ); expect(typeFilterWrapper.prop('chips')[0].key).toEqual('or__type:foo'); const nameFilterWrapper = wrapper.find( - 'DataToolbarFilter[categoryName="Name"]' + 'ToolbarFilter[categoryName="Name"]' ); expect(nameFilterWrapper.prop('chips')[0].key).toEqual('name:bar'); }); @@ -197,19 +194,19 @@ describe('<Search />', () => { }); const onRemove = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${qsConfigNew.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={qsConfigNew} columns={columns} onRemove={onRemove} /> - </DataToolbarContent> - </DataToolbar>, + </ToolbarContent> + </Toolbar>, { context: { router: { history } } } ); expect(history.location.search).toEqual(query); @@ -243,19 +240,19 @@ describe('<Search />', () => { }); const onRemove = jest.fn(); const wrapper = mountWithContexts( - <DataToolbar + <Toolbar id={`${qsConfigNew.namespace}-list-toolbar`} clearAllFilters={() => {}} - collapseListedFiltersBreakpoint="md" + collapseListedFiltersBreakpoint="lg" > - <DataToolbarContent> + <ToolbarContent> <Search qsConfig={qsConfigNew} columns={columns} onRemove={onRemove} /> - </DataToolbarContent> - </DataToolbar>, + </ToolbarContent> + </Toolbar>, { context: { router: { history } } } ); expect(history.location.search).toEqual(query); diff --git a/awx/ui_next/src/components/UserAndTeamAccessAdd/UserAndTeamAccessAdd.test.jsx b/awx/ui_next/src/components/UserAndTeamAccessAdd/UserAndTeamAccessAdd.test.jsx index b80c24a493..b5f986c5db 100644 --- a/awx/ui_next/src/components/UserAndTeamAccessAdd/UserAndTeamAccessAdd.test.jsx +++ b/awx/ui_next/src/components/UserAndTeamAccessAdd/UserAndTeamAccessAdd.test.jsx @@ -56,6 +56,7 @@ describe('<UserAndTeamAccessAdd/>', () => { /> ); }); + await waitForElement(wrapper, 'PFWizard'); }); afterEach(() => { wrapper.unmount(); @@ -68,12 +69,12 @@ describe('<UserAndTeamAccessAdd/>', () => { expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(true); expect( wrapper - .find('WizardNavItem[text="Select items from list"]') + .find('WizardNavItem[content="Select items from list"]') .prop('isDisabled') ).toBe(true); expect( wrapper - .find('WizardNavItem[text="Select roles to apply"]') + .find('WizardNavItem[content="Select roles to apply"]') .prop('isDisabled') ).toBe(true); await act(async () => @@ -90,16 +91,18 @@ describe('<UserAndTeamAccessAdd/>', () => { ); wrapper.update(); expect( - wrapper.find('WizardNavItem[text="Add resource type"]').prop('isDisabled') + wrapper + .find('WizardNavItem[content="Add resource type"]') + .prop('isDisabled') ).toBe(false); expect( wrapper - .find('WizardNavItem[text="Select items from list"]') + .find('WizardNavItem[content="Select items from list"]') .prop('isDisabled') ).toBe(false); expect( wrapper - .find('WizardNavItem[text="Select roles to apply"]') + .find('WizardNavItem[content="Select roles to apply"]') .prop('isDisabled') ).toBe(true); }); diff --git a/awx/ui_next/src/components/Wizard/Wizard.jsx b/awx/ui_next/src/components/Wizard/Wizard.jsx index 99e884baad..9f737d7691 100644 --- a/awx/ui_next/src/components/Wizard/Wizard.jsx +++ b/awx/ui_next/src/components/Wizard/Wizard.jsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; Wizard.displayName = 'PFWizard'; export default styled(Wizard)` - .pf-c-data-toolbar__content { + .pf-c-toolbar__content { padding: 0 !important; } `; diff --git a/awx/ui_next/src/screens/Application/ApplicationAdd/ApplicationAdd.test.jsx b/awx/ui_next/src/screens/Application/ApplicationAdd/ApplicationAdd.test.jsx index bcf39fe64c..2bc0eba47e 100644 --- a/awx/ui_next/src/screens/Application/ApplicationAdd/ApplicationAdd.test.jsx +++ b/awx/ui_next/src/screens/Application/ApplicationAdd/ApplicationAdd.test.jsx @@ -91,8 +91,8 @@ describe('<ApplicationAdd/>', () => { wrapper.update(); expect(wrapper.find('input#name').prop('value')).toBe('new foo'); expect(wrapper.find('input#description').prop('value')).toBe('new bar'); - expect(wrapper.find('InnerChipGroup').length).toBe(1); - expect(wrapper.find('InnerChipGroup').text()).toBe('organization'); + expect(wrapper.find('Chip').length).toBe(1); + expect(wrapper.find('Chip').text()).toBe('organization'); expect( wrapper .find('AnsibleSelect[name="authorization_grant_type"]') diff --git a/awx/ui_next/src/screens/Application/shared/ApplicationForm.test.jsx b/awx/ui_next/src/screens/Application/shared/ApplicationForm.test.jsx index 406c3d5ace..f43fc878d2 100644 --- a/awx/ui_next/src/screens/Application/shared/ApplicationForm.test.jsx +++ b/awx/ui_next/src/screens/Application/shared/ApplicationForm.test.jsx @@ -107,8 +107,8 @@ describe('<ApplicationForm', () => { wrapper.update(); expect(wrapper.find('input#name').prop('value')).toBe('new foo'); expect(wrapper.find('input#description').prop('value')).toBe('new bar'); - expect(wrapper.find('InnerChipGroup').length).toBe(1); - expect(wrapper.find('InnerChipGroup').text()).toBe('organization'); + expect(wrapper.find('Chip').length).toBe(1); + expect(wrapper.find('Chip').text()).toBe('organization'); expect( wrapper .find('AnsibleSelect[name="authorization_grant_type"]') diff --git a/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx b/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx index a0fc4b5f73..e9f79a452a 100644 --- a/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx +++ b/awx/ui_next/src/screens/AuthSetting/AuthSettings.jsx @@ -15,7 +15,9 @@ class AuthSettings extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Authentication Settings`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Authentication Settings`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Credential/Credential.jsx b/awx/ui_next/src/screens/Credential/Credential.jsx index fd318bc07c..baa1e34e0f 100644 --- a/awx/ui_next/src/screens/Credential/Credential.jsx +++ b/awx/ui_next/src/screens/Credential/Credential.jsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { Card, PageSection, CardActions } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import { Switch, useParams, @@ -11,8 +12,6 @@ import { Redirect, Link, } from 'react-router-dom'; -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; import { ResourceAccessList } from '../../components/ResourceAccessList'; import ContentError from '../../components/ContentError'; import RoutedTabs from '../../components/RoutedTabs'; @@ -46,6 +45,16 @@ function Credential({ i18n, setBreadcrumb }) { }, [id, pathname, setBreadcrumb]); const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Credentials`)} + </> + ), + link: `/credentials`, + id: 99, + }, { name: i18n._(t`Details`), link: `/credentials/${id}/details`, id: 0 }, ]; @@ -57,17 +66,10 @@ function Credential({ i18n, setBreadcrumb }) { }); } - let cardHeader = hasContentLoading ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/credentials" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; if (pathname.endsWith('edit') || pathname.endsWith('add')) { - cardHeader = null; + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -90,7 +92,7 @@ function Credential({ i18n, setBreadcrumb }) { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/credentials/:id" diff --git a/awx/ui_next/src/screens/Credential/Credential.test.jsx b/awx/ui_next/src/screens/Credential/Credential.test.jsx index e1308aa750..7cb192ac0c 100644 --- a/awx/ui_next/src/screens/Credential/Credential.test.jsx +++ b/awx/ui_next/src/screens/Credential/Credential.test.jsx @@ -31,7 +31,7 @@ describe('<Credential />', () => { wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 1); + await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 2); }); test('initially renders org-based credential succesfully', async () => { @@ -44,7 +44,7 @@ describe('<Credential />', () => { }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); // org-based credential detail needs access tab - await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 2); + await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 3); }); test('should show content error when user attempts to navigate to erroneous route', async () => { diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx index de814d2630..bc486bb8b1 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx @@ -104,7 +104,9 @@ function CredentialFormFields({ fieldId="credential-credentialType" helperTextInvalid={credTypeMeta.error} isRequired - isValid={!credTypeMeta.touched || !credTypeMeta.error} + validated={ + !credTypeMeta.touched || !credTypeMeta.error ? 'default' : 'error' + } label={i18n._(t`Credential Type`)} > <AnsibleSelect diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx index 2062ed01ee..88a81eaf2c 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx @@ -191,6 +191,12 @@ describe('<CredentialForm />', () => { ).toBe(''); }); test('should show error when error thrown parsing JSON', async () => { + await act(async () => { + await wrapper + .find('AnsibleSelect[id="credential_type"]') + .invoke('onChange')(null, 10); + }); + wrapper.update(); expect(wrapper.find('#credential-gce-file-helper').text()).toBe( 'Select a JSON formatted service account key to autopopulate the following fields.' ); @@ -201,7 +207,15 @@ describe('<CredentialForm />', () => { }); }); wrapper.update(); - expect(wrapper.find('#credential-gce-file-helper').text()).toBe( + expect( + wrapper.find('FormGroup[fieldId="credential-gce-file"]').prop('isValid') + ).toBe(false); + + expect( + wrapper + .find('FormGroup[fieldId="credential-gce-file"]') + .prop('helperTextInvalid') + ).toBe( 'There was an error parsing the file. Please check the file formatting and try again.' ); }); diff --git a/awx/ui_next/src/screens/Credential/shared/TypeInputsSubForm.jsx b/awx/ui_next/src/screens/Credential/shared/TypeInputsSubForm.jsx index d05e27d16e..c0a21b65b5 100644 --- a/awx/ui_next/src/screens/Credential/shared/TypeInputsSubForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/TypeInputsSubForm.jsx @@ -21,7 +21,9 @@ function TypeInputsSubForm({ credentialType, i18n }) { ); return ( <SubFormLayout> - <Title size="md">{i18n._(t`Type Details`)}</Title> + <Title size="md" headingLevel="h4"> + {i18n._(t`Type Details`)} + </Title> <FormColumnLayout> {credentialType.namespace === 'gce' && <GceFileUploadField />} {stringFields.map(fieldOptions => diff --git a/awx/ui_next/src/screens/Dashboard/Dashboard.jsx b/awx/ui_next/src/screens/Dashboard/Dashboard.jsx index 0f2a7ceb79..b8e37fb1ad 100644 --- a/awx/ui_next/src/screens/Dashboard/Dashboard.jsx +++ b/awx/ui_next/src/screens/Dashboard/Dashboard.jsx @@ -15,7 +15,9 @@ class Dashboard extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Dashboard`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Dashboard`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Host/Host.jsx b/awx/ui_next/src/screens/Host/Host.jsx index d52737d195..536ac4f65b 100644 --- a/awx/ui_next/src/screens/Host/Host.jsx +++ b/awx/ui_next/src/screens/Host/Host.jsx @@ -9,10 +9,8 @@ import { useRouteMatch, useLocation, } from 'react-router-dom'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; - -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import RoutedTabs from '../../components/RoutedTabs'; import ContentError from '../../components/ContentError'; import ContentLoading from '../../components/ContentLoading'; @@ -48,6 +46,16 @@ function Host({ i18n, setBreadcrumb }) { const tabsArray = [ { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Hosts`)} + </> + ), + link: `/hosts`, + id: 99, + }, + { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0, @@ -96,17 +104,16 @@ function Host({ i18n, setBreadcrumb }) { ); } + let showCardHeader = true; + + if (location.pathname.endsWith('edit')) { + showCardHeader = false; + } + return ( <PageSection> <Card> - {location.pathname.endsWith('edit') ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/hosts" /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/hosts/:id" to="/hosts/:id/details" exact /> {host && [ diff --git a/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx b/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx index 022247193e..9a973bccc0 100644 --- a/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx +++ b/awx/ui_next/src/screens/InstanceGroup/InstanceGroups.jsx @@ -15,7 +15,9 @@ class InstanceGroups extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Instance Groups`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Instance Groups`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Inventory/Inventory.jsx b/awx/ui_next/src/screens/Inventory/Inventory.jsx index 8515029f4e..2e02d5f03b 100644 --- a/awx/ui_next/src/screens/Inventory/Inventory.jsx +++ b/awx/ui_next/src/screens/Inventory/Inventory.jsx @@ -9,10 +9,8 @@ import { useLocation, useRouteMatch, } from 'react-router-dom'; - -import { Card, CardActions, PageSection } from '@patternfly/react-core'; -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import ContentError from '../../components/ContentError'; import ContentLoading from '../../components/ContentLoading'; import JobList from '../../components/JobList'; @@ -51,6 +49,16 @@ function Inventory({ i18n, setBreadcrumb }) { }, [match.params.id, location.pathname, setBreadcrumb]); const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Inventories`)} + </> + ), + link: `/inventories`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 }, { name: i18n._(t`Groups`), link: `${match.url}/groups`, id: 2 }, @@ -90,19 +98,20 @@ function Inventory({ i18n, setBreadcrumb }) { ); } + let showCardHeader = true; + + if ( + ['edit', 'add', 'groups/', 'hosts/', 'sources/'].some(name => + location.pathname.includes(name) + ) + ) { + showCardHeader = false; + } + return ( <PageSection> <Card> - {['edit', 'add', 'groups/', 'hosts/', 'sources/'].some(name => - location.pathname.includes(name) - ) ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/inventories" /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/inventories/inventory/:id" diff --git a/awx/ui_next/src/screens/Inventory/Inventory.test.jsx b/awx/ui_next/src/screens/Inventory/Inventory.test.jsx index 652ba4cf88..de623f8b53 100644 --- a/awx/ui_next/src/screens/Inventory/Inventory.test.jsx +++ b/awx/ui_next/src/screens/Inventory/Inventory.test.jsx @@ -30,7 +30,7 @@ describe('<Inventory />', () => { wrapper = mountWithContexts(<Inventory setBreadcrumb={() => {}} />); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 6); + await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 7); }); test('should show content error when user attempts to navigate to erroneous route', async () => { diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroup/InventoryGroup.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroup/InventoryGroup.jsx index 28ac2daded..9818bd43de 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroup/InventoryGroup.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroup/InventoryGroup.jsx @@ -10,13 +10,10 @@ import { useLocation, useParams, } from 'react-router-dom'; -import { CardActions } from '@patternfly/react-core'; import { CaretLeftIcon } from '@patternfly/react-icons'; -import CardCloseButton from '../../../components/CardCloseButton'; import RoutedTabs from '../../../components/RoutedTabs'; import ContentError from '../../../components/ContentError'; import ContentLoading from '../../../components/ContentLoading'; -import { TabbedCardHeader } from '../../../components/Card'; import InventoryGroupEdit from '../InventoryGroupEdit/InventoryGroupEdit'; import InventoryGroupDetail from '../InventoryGroupDetail/InventoryGroupDetail'; import InventoryGroupHosts from '../InventoryGroupHosts'; @@ -99,18 +96,14 @@ function InventoryGroup({ i18n, setBreadcrumb, inventory }) { ); } + let showCardHeader = true; + if (['add', 'edit'].some(name => location.pathname.includes(name))) { + showCardHeader = false; + } + return ( <> - {['add', 'edit'].some(name => location.pathname.includes(name)) ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton - linkTo={`/inventories/inventory/${inventory.id}/groups`} - /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/inventories/inventory/:id/groups/:groupId" diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx index fbd02a9ad4..0827f68780 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx @@ -163,9 +163,7 @@ describe('<InventoryGroupsList />', () => { }); wrapper.update(); await act(async () => { - wrapper - .find('DataToolbar Button[aria-label="Delete"]') - .invoke('onClick')(); + wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')(); }); await waitForElement( wrapper, @@ -197,14 +195,12 @@ describe('<InventoryGroupsList />', () => { }); wrapper.update(); await act(async () => { - wrapper - .find('DataToolbar Button[aria-label="Delete"]') - .invoke('onClick')(); + wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')(); }); await waitForElement( wrapper, - 'InventoryGroupsDeleteModal', - el => el.props().isModalOpen === true + 'AlertModal[title="Delete Group?"]', + el => el.props().isOpen === true ); await act(async () => { wrapper.find('Radio[id="radio-delete"]').invoke('onChange')(); @@ -215,7 +211,11 @@ describe('<InventoryGroupsList />', () => { .find('ModalBoxFooter Button[aria-label="Delete"]') .invoke('onClick')(); }); - await waitForElement(wrapper, { title: 'Error!', variant: 'error' }); + await waitForElement( + wrapper, + 'AlertModal[title="Error!"] Modal', + el => el.props().isOpen === true && el.props().title === 'Error!' + ); await act(async () => { wrapper.find('ModalBoxCloseButton').invoke('onClose')(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHost/InventoryHost.jsx b/awx/ui_next/src/screens/Inventory/InventoryHost/InventoryHost.jsx index ef674bc146..33e7884a73 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHost/InventoryHost.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHost/InventoryHost.jsx @@ -9,13 +9,11 @@ import { useRouteMatch, useLocation, } from 'react-router-dom'; -import { Card, CardActions } from '@patternfly/react-core'; +import { Card } from '@patternfly/react-core'; import { CaretLeftIcon } from '@patternfly/react-icons'; import useRequest from '../../../util/useRequest'; import { InventoriesAPI } from '../../../api'; -import { TabbedCardHeader } from '../../../components/Card'; -import CardCloseButton from '../../../components/CardCloseButton'; import ContentError from '../../../components/ContentError'; import ContentLoading from '../../../components/ContentLoading'; import RoutedTabs from '../../../components/RoutedTabs'; @@ -110,16 +108,14 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) { ); } + let showCardHeader = true; + if (['edit'].some(name => location.pathname.includes(name))) { + showCardHeader = false; + } + return ( <> - {['edit'].some(name => location.pathname.includes(name)) ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo={hostListUrl} /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} {isLoading && <ContentLoading />} diff --git a/awx/ui_next/src/screens/Inventory/InventorySource/InventorySource.jsx b/awx/ui_next/src/screens/Inventory/InventorySource/InventorySource.jsx index ecd065a57b..771401b7e4 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySource/InventorySource.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySource/InventorySource.jsx @@ -10,7 +10,6 @@ import { useLocation, } from 'react-router-dom'; import { CaretLeftIcon } from '@patternfly/react-icons'; -import { CardActions } from '@patternfly/react-core'; import useRequest from '../../../util/useRequest'; import { @@ -18,9 +17,7 @@ import { InventorySourcesAPI, OrganizationsAPI, } from '../../../api'; -import { TabbedCardHeader } from '../../../components/Card'; import { Schedules } from '../../../components/Schedule'; -import CardCloseButton from '../../../components/CardCloseButton'; import ContentError from '../../../components/ContentError'; import ContentLoading from '../../../components/ContentLoading'; import RoutedTabs from '../../../components/RoutedTabs'; @@ -112,18 +109,15 @@ function InventorySource({ i18n, inventory, setBreadcrumb, me }) { return <ContentError error={error} />; } + let showCardHeader = true; + + if (['edit', 'schedules/'].some(name => location.pathname.includes(name))) { + showCardHeader = false; + } + return ( <> - {['edit', 'schedules/'].some(name => - location.pathname.includes(name) - ) ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo={sourceListUrl} /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} {isLoading && <ContentLoading />} diff --git a/awx/ui_next/src/screens/Inventory/SmartInventory.jsx b/awx/ui_next/src/screens/Inventory/SmartInventory.jsx index 29e503faa9..f35876c768 100644 --- a/awx/ui_next/src/screens/Inventory/SmartInventory.jsx +++ b/awx/ui_next/src/screens/Inventory/SmartInventory.jsx @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import { Switch, Route, Redirect, withRouter, Link } from 'react-router-dom'; -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; import ContentError from '../../components/ContentError'; import JobList from '../../components/JobList'; import RoutedTabs from '../../components/RoutedTabs'; @@ -64,6 +63,16 @@ class SmartInventory extends Component { const { contentError, hasContentLoading, inventory } = this.state; const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Inventories`)} + </> + ), + link: `/inventories`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 }, { name: i18n._(t`Hosts`), link: `${match.url}/hosts`, id: 2 }, @@ -74,17 +83,10 @@ class SmartInventory extends Component { }, ]; - let cardHeader = hasContentLoading ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/inventories" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; if (location.pathname.endsWith('edit')) { - cardHeader = null; + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -108,7 +110,7 @@ class SmartInventory extends Component { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/inventories/smart_inventory/:id" diff --git a/awx/ui_next/src/screens/Inventory/SmartInventory.test.jsx b/awx/ui_next/src/screens/Inventory/SmartInventory.test.jsx index 6483543fd7..e229e1bda8 100644 --- a/awx/ui_next/src/screens/Inventory/SmartInventory.test.jsx +++ b/awx/ui_next/src/screens/Inventory/SmartInventory.test.jsx @@ -29,7 +29,7 @@ describe('<SmartInventory />', () => { 'SmartInventory', el => el.state('hasContentLoading') === false ); - await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 4); + await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 5); done(); }); test('should show content error when user attempts to navigate to erroneous route', async () => { diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx index ffcab9f148..73dec8877f 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceForm.jsx @@ -117,7 +117,9 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => { fieldId="source" helperTextInvalid={sourceMeta.error} isRequired - isValid={!sourceMeta.touched || !sourceMeta.error} + validated={ + !sourceMeta.touched || !sourceMeta.error ? 'default' : 'error' + } label={i18n._(t`Source`)} > <AnsibleSelect @@ -161,7 +163,9 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => { )} {sourceField.value !== '' && ( <SubFormLayout> - <Title size="md">{i18n._(t`Source details`)}</Title> + <Title size="md" headingLevel="h4"> + {i18n._(t`Source details`)} + </Title> <FormColumnLayout> { { diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.jsx index e9fe7f6690..3689e305a8 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SCMSubForm.jsx @@ -83,9 +83,11 @@ const SCMSubForm = ({ i18n }) => { <FormGroup fieldId="source_path" helperTextInvalid={sourcePathError?.message || sourcePathMeta.error} - isValid={ + validated={ (!sourcePathMeta.error || !sourcePathMeta.touched) && !sourcePathError?.message + ? 'default' + : 'error' } isRequired label={i18n._(t`Inventory file`)} diff --git a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SharedFields.jsx index 698799ab62..b4cca4a073 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SharedFields.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventorySourceSubForms/SharedFields.jsx @@ -229,7 +229,7 @@ export const VerbosityField = withI18n()(({ i18n }) => { return ( <FormGroup fieldId="verbosity" - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={i18n._(t`Verbosity`)} > <FieldTooltip diff --git a/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx b/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx index 9fe0aea136..d45f660fa8 100644 --- a/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx +++ b/awx/ui_next/src/screens/InventoryScript/InventoryScripts.jsx @@ -15,7 +15,9 @@ class InventoryScripts extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Inventory Scripts`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Inventory Scripts`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Job/Job.jsx b/awx/ui_next/src/screens/Job/Job.jsx index 624f7443ff..924ebfebcc 100644 --- a/awx/ui_next/src/screens/Job/Job.jsx +++ b/awx/ui_next/src/screens/Job/Job.jsx @@ -2,11 +2,10 @@ import React, { Component } from 'react'; import { Route, withRouter, Switch, Redirect, Link } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import { JobsAPI } from '../../api'; -import { TabbedCardHeader } from '../../components/Card'; import ContentError from '../../components/ContentError'; -import CardCloseButton from '../../components/CardCloseButton'; import RoutedTabs from '../../components/RoutedTabs'; import JobDetail from './JobDetail'; @@ -67,21 +66,24 @@ class Job extends Component { } const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Jobs`)} + </> + ), + link: `/jobs`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Output`), link: `${match.url}/output`, id: 1 }, ]; - let cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/jobs" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; if (!isInitialized) { - cardHeader = null; + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -117,7 +119,7 @@ class Job extends Component { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/jobs/:type/:id" diff --git a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx index 7a9f1c9399..f82e12eb00 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Modal as PFModal, Tab, Tabs as PFTabs } from '@patternfly/react-core'; +import { Modal, Tab, Tabs, TabTitleText } from '@patternfly/react-core'; import PropTypes from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; @@ -12,18 +12,6 @@ import CodeMirrorInput from '../../../components/CodeMirrorInput'; const entities = new AllHtmlEntities(); -const Modal = styled(PFModal)` - --pf-c-modal-box__footer--MarginTop: 0; - align-self: flex-start; - margin-top: 200px; - .pf-c-modal-box__body { - overflow-y: hidden; - } - .pf-c-tab-content { - padding: 24px 0; - } -`; - const HostNameDetailValue = styled.div` align-items: center; display: inline-grid; @@ -31,31 +19,6 @@ const HostNameDetailValue = styled.div` grid-template-columns: auto auto; `; -const Tabs = styled(PFTabs)` - --pf-c-tabs__button--PaddingLeft: 20px; - --pf-c-tabs__button--PaddingRight: 20px; - - .pf-c-tabs__list { - li:first-of-type .pf-c-tabs__button { - &::after { - margin-left: 0; - } - } - } - - &:not(.pf-c-tabs__item)::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - content: ''; - border-bottom: solid var(--pf-c-tabs__item--BorderColor); - border-width: var(--pf-c-tabs__item--BorderWidth) 0 - var(--pf-c-tabs__item--BorderWidth) 0; - } -`; - const processEventStatus = event => { let status = null; if (event.event === 'runner_on_unreachable') { @@ -133,11 +96,11 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) { return ( <Modal - isFooterLeftAligned - isLarge isOpen={isOpen} onClose={onClose} title={i18n._(t`Host Details`)} + aria-label={i18n._(t`Host details modal`)} + width="75%" > <Tabs aria-label={i18n._(t`Tabs`)} @@ -147,9 +110,12 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) { <Tab aria-label={i18n._(t`Details tab`)} eventKey={0} - title={i18n._(t`Details`)} + title={<TabTitleText>{i18n._(t`Details`)}</TabTitleText>} > - <DetailList style={{ alignItems: 'center' }} gutter="sm"> + <DetailList + style={{ alignItems: 'center', marginTop: '20px' }} + gutter="sm" + > <Detail label={i18n._(t`Host Name`)} value={ @@ -175,7 +141,7 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) { </Tab> <Tab eventKey={1} - title={i18n._(t`JSON`)} + title={<TabTitleText>{i18n._(t`JSON`)}</TabTitleText>} aria-label={i18n._(t`JSON tab`)} > {activeTabKey === 1 && jsonObj ? ( @@ -193,7 +159,7 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) { </Tab> <Tab eventKey={2} - title={i18n._(t`Standard Out`)} + title={<TabTitleText>{i18n._(t`Standard Out`)}</TabTitleText>} aria-label={i18n._(t`Standard out tab`)} > {activeTabKey === 2 && stdOut ? ( @@ -211,7 +177,7 @@ function HostEventModal({ onClose, hostEvent = {}, isOpen = false, i18n }) { </Tab> <Tab eventKey={3} - title={i18n._(t`Standard Error`)} + title={<TabTitleText>{i18n._(t`Standard Error`)}</TabTitleText>} aria-label={i18n._(t`Standard error tab`)} > {activeTabKey === 3 && stdErr ? ( diff --git a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx index 0a18cc3d93..01b6ed61bd 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx @@ -88,16 +88,7 @@ describe('HostEventModal', () => { ); /* eslint-disable react/button-has-type */ - expect( - wrapper - .find('Tabs') - .containsAllMatchingElements([ - <button aria-label="Details tab">Details</button>, - <button aria-label="JSON tab">JSON</button>, - <button aria-label="Standard out tab">Standard Out</button>, - <button aria-label="Standard error tab">Standard Error</button>, - ]) - ).toEqual(true); + expect(wrapper.find('Tabs TabButton').length).toEqual(4); }); test('should show details tab content on mount', () => { diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx index 4e72d0014c..9c6b324891 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx @@ -475,6 +475,7 @@ class JobOutput extends Component { variant="danger" onClose={() => this.setState({ deletionError: null })} title={i18n._(t`Job Delete Error`)} + label={i18n._(t`Job Delete Error`)} > <ErrorDetail error={deletionError} /> </AlertModal> diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx index 5afa5d2573..5531a0ade6 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.test.jsx @@ -338,7 +338,7 @@ describe('<JobOutput />', () => { wrapper.find('Modal button[aria-label="Delete"]').simulate('click'); await waitForElement(wrapper, 'Modal ErrorDetail'); const errorModalCloseBtn = wrapper.find( - 'ModalBox div[aria-label="Job Delete Error"] button[aria-label="Close"]' + 'ModalBox[aria-label="Job Delete Error"] ModalBoxCloseButton' ); errorModalCloseBtn.simulate('click'); await waitForElement(wrapper, 'Modal ErrorDetail', el => el.length === 0); diff --git a/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx b/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx index ded44676f6..f5fb77ef50 100644 --- a/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx +++ b/awx/ui_next/src/screens/JobsSetting/JobsSettings.jsx @@ -15,7 +15,9 @@ class JobsSettings extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Jobs Settings`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Jobs Settings`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/License/License.jsx b/awx/ui_next/src/screens/License/License.jsx index cebf2a4734..1ec59d2930 100644 --- a/awx/ui_next/src/screens/License/License.jsx +++ b/awx/ui_next/src/screens/License/License.jsx @@ -15,7 +15,9 @@ class License extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`License`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`License`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx index f86e9b3040..658cfcb403 100644 --- a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx +++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx @@ -15,7 +15,9 @@ class ManagementJobs extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Management Jobs`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Management Jobs`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx index abcf1ab511..857201bc6b 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplates.jsx @@ -15,7 +15,9 @@ class NotificationTemplates extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`Notification Templates`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`Notification Templates`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Organization/Organization.jsx b/awx/ui_next/src/screens/Organization/Organization.jsx index 6dfb120c29..248f5fc430 100644 --- a/awx/ui_next/src/screens/Organization/Organization.jsx +++ b/awx/ui_next/src/screens/Organization/Organization.jsx @@ -2,9 +2,8 @@ import React, { Component } from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Switch, Route, withRouter, Redirect, Link } from 'react-router-dom'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; -import CardCloseButton from '../../components/CardCloseButton'; -import { TabbedCardHeader } from '../../components/Card'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import RoutedTabs from '../../components/RoutedTabs'; import ContentError from '../../components/ContentError'; import NotificationList from '../../components/NotificationList/NotificationList'; @@ -116,6 +115,16 @@ class Organization extends Component { (me.is_system_auditor || isAuditorOfThisOrg || isAdminOfThisOrg); const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Organizations`)} + </> + ), + link: `/organizations`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 }, { name: i18n._(t`Teams`), link: `${match.url}/teams`, id: 2 }, @@ -129,21 +138,10 @@ class Organization extends Component { }); } - let cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/organizations" /> - </CardActions> - </TabbedCardHeader> - ); - - if (!isInitialized) { - cardHeader = null; - } + let showCardHeader = true; - if (location.pathname.endsWith('edit')) { - cardHeader = null; + if (!isInitialized || location.pathname.endsWith('edit')) { + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -168,7 +166,7 @@ class Organization extends Component { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/organizations/:id" diff --git a/awx/ui_next/src/screens/Organization/Organization.test.jsx b/awx/ui_next/src/screens/Organization/Organization.test.jsx index a287b87f85..298ae9188e 100644 --- a/awx/ui_next/src/screens/Organization/Organization.test.jsx +++ b/awx/ui_next/src/screens/Organization/Organization.test.jsx @@ -53,7 +53,7 @@ describe('<Organization />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 4 + el => el.length === 5 ); expect(tabs.last().text()).toEqual('Notifications'); done(); @@ -74,7 +74,7 @@ describe('<Organization />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 3 + el => el.length === 4 ); tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications')); done(); diff --git a/awx/ui_next/src/screens/Portal/Portal.jsx b/awx/ui_next/src/screens/Portal/Portal.jsx index d5f6cbecec..6651fc25d2 100644 --- a/awx/ui_next/src/screens/Portal/Portal.jsx +++ b/awx/ui_next/src/screens/Portal/Portal.jsx @@ -15,7 +15,9 @@ class Portal extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`My View`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`My View`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Project/Project.jsx b/awx/ui_next/src/screens/Project/Project.jsx index f20b66e3ca..3dae7a5a6c 100644 --- a/awx/ui_next/src/screens/Project/Project.jsx +++ b/awx/ui_next/src/screens/Project/Project.jsx @@ -2,9 +2,8 @@ import React, { Component } from 'react'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Switch, Route, withRouter, Redirect, Link } from 'react-router-dom'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import RoutedTabs from '../../components/RoutedTabs'; import ContentError from '../../components/ContentError'; import NotificationList from '../../components/NotificationList'; @@ -121,6 +120,16 @@ class Project extends Component { const canToggleNotifications = isNotifAdmin; const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Projects`)} + </> + ), + link: `/projects`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details` }, { name: i18n._(t`Access`), link: `${match.url}/access` }, ]; @@ -148,24 +157,14 @@ class Project extends Component { tab.id = n; }); - let cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/projects" /> - </CardActions> - </TabbedCardHeader> - ); - - if (!isInitialized) { - cardHeader = null; - } + let showCardHeader = true; if ( + !isInitialized || location.pathname.endsWith('edit') || location.pathname.includes('schedules/') ) { - cardHeader = null; + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -188,7 +187,7 @@ class Project extends Component { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/projects/:id" to="/projects/:id/details" exact /> {project && ( diff --git a/awx/ui_next/src/screens/Project/Project.test.jsx b/awx/ui_next/src/screens/Project/Project.test.jsx index f2ecf09cc7..7d8080e8a9 100644 --- a/awx/ui_next/src/screens/Project/Project.test.jsx +++ b/awx/ui_next/src/screens/Project/Project.test.jsx @@ -44,9 +44,9 @@ describe('<Project />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 5 + el => el.length === 6 ); - expect(tabs.at(2).text()).toEqual('Notifications'); + expect(tabs.at(3).text()).toEqual('Notifications'); done(); }); @@ -65,7 +65,7 @@ describe('<Project />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 4 + el => el.length === 5 ); tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications')); done(); @@ -86,9 +86,9 @@ describe('<Project />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 4 + el => el.length === 5 ); - expect(tabs.at(3).text()).toEqual('Schedules'); + expect(tabs.at(4).text()).toEqual('Schedules'); done(); }); @@ -108,7 +108,7 @@ describe('<Project />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 3 + el => el.length === 4 ); tabs.forEach(tab => expect(tab.text()).not.toEqual('Schedules')); done(); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx index eee485d646..692c66d3d2 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx @@ -172,7 +172,9 @@ function ProjectFormFields({ fieldId="project-scm-type" helperTextInvalid={scmTypeMeta.error} isRequired - isValid={!scmTypeMeta.touched || !scmTypeMeta.error} + validated={ + !scmTypeMeta.touched || !scmTypeMeta.error ? 'default' : 'error' + } label={i18n._(t`Source Control Credential Type`)} > <AnsibleSelect @@ -204,7 +206,9 @@ function ProjectFormFields({ </FormGroup> {formik.values.scm_type !== '' && ( <SubFormLayout> - <Title size="md">{i18n._(t`Type Details`)}</Title> + <Title size="md" headingLevel="h4"> + {i18n._(t`Type Details`)} + </Title> <FormColumnLayout> { { diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx index 96646238d9..b6ece76103 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx @@ -81,7 +81,7 @@ const ManualSubForm = ({ fieldId="project-local-path" helperTextInvalid={pathMeta.error} isRequired - isValid={!pathMeta.touched || !pathMeta.error} + validated={!pathMeta.touched || !pathMeta.error ? 'default' : 'error'} label={i18n._(t`Playbook Directory`)} > <FieldTooltip diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx index 0833c9e49d..4956c1cab1 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx @@ -104,7 +104,9 @@ export const ScmTypeOptions = withI18n()( {scmUpdateOnLaunch && ( <> - <Title size="md">{i18n._(t`Option Details`)}</Title> + <Title size="md" headingLevel="h4"> + {i18n._(t`Option Details`)} + </Title> <FormField id="project-cache-timeout" name="scm_update_cache_timeout" diff --git a/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx b/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx index d81dda3ea2..458658e23c 100644 --- a/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx +++ b/awx/ui_next/src/screens/SystemSetting/SystemSettings.jsx @@ -15,7 +15,9 @@ class SystemSettings extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`System Settings`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`System Settings`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/Team/Team.jsx b/awx/ui_next/src/screens/Team/Team.jsx index b7e878b415..c3366af422 100644 --- a/awx/ui_next/src/screens/Team/Team.jsx +++ b/awx/ui_next/src/screens/Team/Team.jsx @@ -9,9 +9,8 @@ import { useLocation, useParams, } from 'react-router-dom'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; -import CardCloseButton from '../../components/CardCloseButton'; -import { TabbedCardHeader } from '../../components/Card'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import RoutedTabs from '../../components/RoutedTabs'; import ContentError from '../../components/ContentError'; import TeamDetail from './TeamDetail'; @@ -41,22 +40,25 @@ function Team({ i18n, setBreadcrumb }) { }, [id, setBreadcrumb, location]); const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Teams`)} + </> + ), + link: `/teams`, + id: 99, + }, { name: i18n._(t`Details`), link: `/teams/${id}/details`, id: 0 }, { name: i18n._(t`Users`), link: `/teams/${id}/users`, id: 1 }, { name: i18n._(t`Access`), link: `/teams/${id}/access`, id: 2 }, ]; - let cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/teams" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; if (location.pathname.endsWith('edit')) { - cardHeader = null; + showCardHeader = false; } if (!hasContentLoading && contentError) { @@ -79,7 +81,7 @@ function Team({ i18n, setBreadcrumb }) { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/teams/:id" to="/teams/:id/details" exact /> {team && ( diff --git a/awx/ui_next/src/screens/Team/TeamAccess/TeamAccessListItem.jsx b/awx/ui_next/src/screens/Team/TeamAccess/TeamAccessListItem.jsx index 12495c8003..e32199d19e 100644 --- a/awx/ui_next/src/screens/Team/TeamAccess/TeamAccessListItem.jsx +++ b/awx/ui_next/src/screens/Team/TeamAccess/TeamAccessListItem.jsx @@ -40,12 +40,12 @@ function TeamAccessListItem({ role, i18n, detailUrl, onSelect }) { label={i18n._(t`Role`)} value={ <Chip - isReadOnly={ - !role.summary_fields.user_capabilities.unattach - } key={role.name} aria-label={role.name} onClick={() => onSelect(role)} + isReadOnly={ + !role.summary_fields.user_capabilities.unattach + } > {role.name} </Chip> diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyList.test.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyList.test.jsx index 755956a5f4..bdc70c4fff 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyList.test.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyList.test.jsx @@ -142,7 +142,7 @@ describe('<SurveyList />', () => { expect( wrapper - .find('DataToolbar') + .find('Toolbar') .find('Checkbox') .prop('isDisabled') ).toBe(true); diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.jsx index e9d8f0a1d2..f26f1f8ed1 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.jsx @@ -30,9 +30,10 @@ function SurveyPreviewModal({ return ( <Modal title={i18n._(t`Survey Preview`)} + aria-label={i18n._(t`Survey preview modal`)} isOpen={isPreviewModalOpen} onClose={() => onToggleModalOpen(false)} - isSmall + variant="small" > <Formik initialValues={initialValues}> {() => ( @@ -97,7 +98,7 @@ function SurveyPreviewModal({ isDisabled isReadOnly variant={SelectVariant.typeaheadMulti} - isExpanded={false} + isOpen={false} selections={q.default.length > 0 && q.default.split('\n')} onToggle={() => {}} aria-label={i18n._(t`Multi-Select`)} diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.test.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.test.jsx index ec56a7c7b7..74aac44a3f 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.test.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyPreviewModal.test.jsx @@ -106,19 +106,20 @@ describe('<SurveyPreviewModal />', () => { .find('Select[aria-label="Multi-Select"]') .find('Chip'); - expect(question1.text()).toBe('Text Question'); + expect(question1.text()).toBe('Text Question '); expect(question1Value.prop('value')).toBe('Text Question Value'); expect(question1Value.prop('isDisabled')).toBe(true); expect(question2.text()).toBe('Select Question'); - expect(question2Value.find('span').text()).toBe('Select Question Value'); + expect(question2Value.find('.pf-c-select__toggle-text').text()).toBe( + 'Select Question Value' + ); expect(question2Value.prop('isDisabled')).toBe(true); expect(question3.text()).toBe('Text Area Question'); expect(question3Value.prop('value')).toBe('Text Area Question Value'); expect(question3Value.prop('disabled')).toBe(true); - - expect(question4.text()).toBe('Password Question'); + expect(question4.text()).toBe('Password Question '); expect(question4Value.prop('placeholder')).toBe('ENCRYPTED'); expect(question4Value.prop('isDisabled')).toBe(true); diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.jsx index af6144025a..16eac2be70 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.jsx @@ -5,15 +5,17 @@ import { withI18n } from '@lingui/react'; import styled from 'styled-components'; import { - DataToolbar as _DataToolbar, - DataToolbarContent, - DataToolbarGroup, - DataToolbarItem, -} from '@patternfly/react-core/dist/umd/experimental'; -import { Switch, Checkbox, Button } from '@patternfly/react-core'; + Switch, + Checkbox, + Button, + Toolbar as _Toolbar, + ToolbarContent, + ToolbarGroup, + ToolbarItem, +} from '@patternfly/react-core'; import { ToolbarAddButton } from '../../../components/PaginatedDataList'; -const DataToolbar = styled(_DataToolbar)` +const Toolbar = styled(_Toolbar)` margin-left: 52px; `; @@ -30,9 +32,9 @@ function SurveyToolbar({ isDeleteDisabled = !canEdit || isDeleteDisabled; const match = useRouteMatch(); return ( - <DataToolbar id="survey-toolbar"> - <DataToolbarContent> - <DataToolbarItem> + <Toolbar id="survey-toolbar"> + <ToolbarContent> + <ToolbarItem> <Checkbox isDisabled={!canEdit} isChecked={isAllSelected} @@ -42,8 +44,8 @@ function SurveyToolbar({ aria-label={i18n._(t`Select all`)} id="select-all" /> - </DataToolbarItem> - <DataToolbarItem> + </ToolbarItem> + <ToolbarItem> <Switch aria-label={i18n._(t`Survey Toggle`)} id="survey-toggle" @@ -53,15 +55,15 @@ function SurveyToolbar({ isDisabled={!canEdit} onChange={() => onToggleSurvey(!surveyEnabled)} /> - </DataToolbarItem> - <DataToolbarGroup> - <DataToolbarItem> + </ToolbarItem> + <ToolbarGroup> + <ToolbarItem> <ToolbarAddButton isDisabled={!canEdit} linkTo={`${match.url}/add`} /> - </DataToolbarItem> - <DataToolbarItem> + </ToolbarItem> + <ToolbarItem> <Button variant="danger" isDisabled={isDeleteDisabled} @@ -69,10 +71,10 @@ function SurveyToolbar({ > {i18n._(t`Delete`)} </Button> - </DataToolbarItem> - </DataToolbarGroup> - </DataToolbarContent> - </DataToolbar> + </ToolbarItem> + </ToolbarGroup> + </ToolbarContent> + </Toolbar> ); } diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.test.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.test.jsx index 8fb4857525..2311f1d443 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.test.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyToolbar.test.jsx @@ -102,7 +102,7 @@ describe('<SurveyToolbar />', () => { }); expect( wrapper - .find('DataToolbar') + .find('Toolbar') .find('Checkbox') .prop('isDisabled') ).toBe(true); diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index 7110f025fc..5bd84e8383 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -1,7 +1,8 @@ import React, { useEffect, useCallback } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import { Switch, Route, @@ -11,14 +12,11 @@ import { useParams, useRouteMatch, } from 'react-router-dom'; +import RoutedTabs from '../../components/RoutedTabs'; import useRequest from '../../util/useRequest'; - -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; import ContentError from '../../components/ContentError'; import JobList from '../../components/JobList'; import NotificationList from '../../components/NotificationList'; -import RoutedTabs from '../../components/RoutedTabs'; import { Schedules } from '../../components/Schedule'; import { ResourceAccessList } from '../../components/ResourceAccessList'; import JobTemplateDetail from './JobTemplateDetail'; @@ -82,6 +80,16 @@ function Template({ i18n, me, setBreadcrumb }) { template?.summary_fields?.user_capabilities.delete; const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Templates`)} + </> + ), + link: `/templates`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details` }, { name: i18n._(t`Access`), link: `${match.url}/access` }, ]; @@ -115,19 +123,13 @@ function Template({ i18n, me, setBreadcrumb }) { tab.id = n; }); - let cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/templates" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; + if ( location.pathname.endsWith('edit') || location.pathname.includes('schedules/') ) { - cardHeader = null; + showCardHeader = false; } const contentError = rolesAndTemplateError; @@ -151,7 +153,7 @@ function Template({ i18n, me, setBreadcrumb }) { return ( <PageSection> <Card> - {cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/templates/:templateType/:id" diff --git a/awx/ui_next/src/screens/Template/Template.test.jsx b/awx/ui_next/src/screens/Template/Template.test.jsx index 0495fdd9bf..8176e4486a 100644 --- a/awx/ui_next/src/screens/Template/Template.test.jsx +++ b/awx/ui_next/src/screens/Template/Template.test.jsx @@ -59,9 +59,9 @@ describe('<Template />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 6 + el => el.length === 7 ); - expect(tabs.at(2).text()).toEqual('Notifications'); + expect(tabs.at(3).text()).toEqual('Notifications'); done(); }); test('notifications tab hidden with reduced permissions', async done => { @@ -83,7 +83,7 @@ describe('<Template />', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 5 + el => el.length === 6 ); tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications')); done(); diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx index eb40b93f4f..9706f89ae2 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx @@ -1,11 +1,10 @@ import React, { Component } from 'react'; import { t } from '@lingui/macro'; import { withI18n } from '@lingui/react'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import { Switch, Route, Redirect, withRouter, Link } from 'react-router-dom'; -import { TabbedCardHeader } from '../../components/Card'; import AppendBody from '../../components/AppendBody'; -import CardCloseButton from '../../components/CardCloseButton'; import ContentError from '../../components/ContentError'; import FullPage from '../../components/FullPage'; import JobList from '../../components/JobList'; @@ -121,6 +120,16 @@ class WorkflowJobTemplate extends Component { template?.summary_fields?.user_capabilities.delete; const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Templates`)} + </> + ), + link: `/templates`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details` }, { name: i18n._(t`Access`), link: `${match.url}/access` }, ]; @@ -183,22 +192,19 @@ class WorkflowJobTemplate extends Component { ); } - const cardHeader = ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo="/templates" /> - </CardActions> - </TabbedCardHeader> - ); + let showCardHeader = true; + + if ( + location.pathname.endsWith('edit') || + location.pathname.includes('schedules/') + ) { + showCardHeader = false; + } return ( <PageSection> <Card> - {location.pathname.endsWith('edit') || - location.pathname.includes('schedules/') - ? null - : cardHeader} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} <Switch> <Redirect from="/templates/workflow_job_template/:id" diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.test.jsx index 9930643538..5217dff7c5 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.test.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.test.jsx @@ -131,11 +131,11 @@ describe('<WorkflowJobTemplateDetail/>', () => { const organization = wrapper .find('Detail[label="Organization"]') - .find('span'); + .find('.pf-c-label__content'); const inventory = wrapper.find('Detail[label="Inventory"]').find('a'); const labels = wrapper .find('Detail[label="Labels"]') - .find('Chip[component="li"]'); + .find('Chip[component="div"]'); const sparkline = wrapper.find('Sparkline Link'); expect(organization.text()).toBe('Org'); diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkAddModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkAddModal.jsx index 97a941755e..5d7baf4099 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkAddModal.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkAddModal.jsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { BaseSizes, Title, TitleLevel } from '@patternfly/react-core'; +import { Title } from '@patternfly/react-core'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { WorkflowDispatchContext } from '../../../../../contexts/Workflow'; @@ -10,7 +10,7 @@ function LinkAddModal({ i18n }) { return ( <LinkModal header={ - <Title headingLevel={TitleLevel.h1} size={BaseSizes['2xl']}> + <Title headingLevel="h1" size="xl"> {i18n._(t`Add Link`)} </Title> } diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkEditModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkEditModal.jsx index 177e11bb32..8c4259b73a 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkEditModal.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkEditModal.jsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { BaseSizes, Title, TitleLevel } from '@patternfly/react-core'; +import { Title } from '@patternfly/react-core'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { WorkflowDispatchContext } from '../../../../../contexts/Workflow'; @@ -10,7 +10,7 @@ function LinkEditModal({ i18n }) { return ( <LinkModal header={ - <Title headingLevel={TitleLevel.h1} size={BaseSizes['2xl']}> + <Title headingLevel="h1" size="xl"> {i18n._(t`Edit Link`)} </Title> } diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkModal.jsx index 0cf4601c4c..ed11bf59c2 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkModal.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/LinkModals/LinkModal.jsx @@ -20,8 +20,8 @@ function LinkModal({ header, i18n, onConfirm }) { width={600} header={header} isOpen - isFooterLeftAligned title={i18n._(t`Workflow Link`)} + aria-label={i18n._(t`Workflow link modal`)} onClose={() => dispatch({ type: 'CANCEL_LINK_MODAL' })} actions={[ <Button diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx index 39f410f338..405e8a91e4 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.jsx @@ -4,7 +4,10 @@ import { WorkflowDispatchContext, WorkflowStateContext, } from '../../../../../contexts/Workflow'; -import { mountWithContexts } from '../../../../../../testUtils/enzymeHelpers'; +import { + waitForElement, + mountWithContexts, +} from '../../../../../../testUtils/enzymeHelpers'; import { InventorySourcesAPI, JobTemplatesAPI, @@ -95,6 +98,7 @@ describe('NodeModal', () => { </WorkflowDispatchContext.Provider> ); }); + await waitForElement(wrapper, 'PFWizard'); }); afterAll(() => { @@ -307,6 +311,7 @@ describe('NodeModal', () => { </WorkflowDispatchContext.Provider> ); }); + await waitForElement(wrapper, 'PFWizard'); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('project_sync'); await act(async () => { wrapper.find('AnsibleSelect').prop('onChange')(null, 'approval'); @@ -388,6 +393,7 @@ describe('NodeModal', () => { </WorkflowDispatchContext.Provider> ); }); + await waitForElement(wrapper, 'PFWizard'); expect(wrapper.find('AnsibleSelect').prop('value')).toBe('approval'); await act(async () => { wrapper.find('AnsibleSelect').prop('onChange')( diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx index 146485d4cb..e9ee7928bb 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/NodeTypeStep.jsx @@ -131,14 +131,14 @@ function NodeTypeStep({ <FormGroup fieldId="approval-name" isRequired - isValid={isValid} + validated={isValid ? 'default' : 'error'} label={i18n._(t`Name`)} > <TextInput autoFocus id="approval-name" isRequired - isValid={isValid} + validated={isValid ? 'default' : 'error'} type="text" {...field} onChange={(value, evt) => { diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx index 281c7b0368..6eb01a9a0e 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeViewModal.jsx @@ -131,10 +131,10 @@ function NodeViewModal({ i18n }) { return ( <Modal - isLarge + variant="large" isOpen - isFooterLeftAligned title={unifiedJobTemplate.name} + aria-label={i18n._(t`Workflow node view modal`)} onClose={() => dispatch({ type: 'SET_NODE_TO_VIEW', value: null })} actions={[ <Button diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/UnsavedChangesModal.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/UnsavedChangesModal.jsx index d06803ee53..84316595c3 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/UnsavedChangesModal.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/UnsavedChangesModal.jsx @@ -11,8 +11,8 @@ function UnsavedChangesModal({ i18n, onSaveAndExit, onExit }) { <Modal width={600} isOpen - isFooterLeftAligned title={i18n._(t`Warning: Unsaved Changes`)} + aria-label={i18n._(t`Unsaved changes modal`)} onClose={() => dispatch({ type: 'TOGGLE_UNSAVED_CHANGES_MODAL' })} actions={[ <Button diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx index 7f93f9a315..0857313036 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx @@ -68,7 +68,11 @@ function VisualizerToolbar({ return ( <div id="visualizer-toolbar"> <div css="align-items: center; border-bottom: 1px solid grey; display: flex; height: 56px; padding: 0px 20px;"> - <Title size="xl" id="visualizer-toolbar-template-name"> + <Title + headingLevel="h2" + size="xl" + id="visualizer-toolbar-template-name" + > {template.name} </Title> <div css="align-items: center; display: flex; flex: 1; justify-content: flex-end"> diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index 7bd4409fde..ca5ad56073 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -291,7 +291,9 @@ function JobTemplateForm({ <FormGroup fieldId="template-playbook" helperTextInvalid={playbookMeta.error} - isValid={!playbookMeta.touched || !playbookMeta.error} + validated={ + !playbookMeta.touched || !playbookMeta.error ? 'default' : 'error' + } isRequired label={i18n._(t`Playbook`)} > @@ -381,7 +383,9 @@ function JobTemplateForm({ <TextInput id="template-limit" {...limitField} - isValid={!limitMeta.touched || !limitMeta.error} + validated={ + !limitMeta.touched || !limitMeta.error ? 'default' : 'error' + } onChange={value => { limitHelpers.setValue(value); }} diff --git a/awx/ui_next/src/screens/Template/shared/LabelSelect.jsx b/awx/ui_next/src/screens/Template/shared/LabelSelect.jsx index d1f8de5ecc..539947738f 100644 --- a/awx/ui_next/src/screens/Template/shared/LabelSelect.jsx +++ b/awx/ui_next/src/screens/Template/shared/LabelSelect.jsx @@ -83,8 +83,8 @@ function LabelSelect({ value, placeholder, onChange, onError, createText }) { }} isDisabled={isLoading} selections={selections} - isExpanded={isExpanded} - ariaLabelledBy="label-select" + isOpen={isExpanded} + aria-labelledby="label-select" placeholderText={placeholder} createText={createText} > diff --git a/awx/ui_next/src/screens/UISetting/UISettings.jsx b/awx/ui_next/src/screens/UISetting/UISettings.jsx index 1905463cd2..1ecec2af54 100644 --- a/awx/ui_next/src/screens/UISetting/UISettings.jsx +++ b/awx/ui_next/src/screens/UISetting/UISettings.jsx @@ -15,7 +15,9 @@ class UISettings extends Component { return ( <Fragment> <PageSection variant={light} className="pf-m-condensed"> - <Title size="2xl">{i18n._(t`User Interface Settings`)}</Title> + <Title size="2xl" headingLevel="h2"> + {i18n._(t`User Interface Settings`)} + </Title> </PageSection> <PageSection /> </Fragment> diff --git a/awx/ui_next/src/screens/User/User.jsx b/awx/ui_next/src/screens/User/User.jsx index 5e195da2f3..1aa8bca032 100644 --- a/awx/ui_next/src/screens/User/User.jsx +++ b/awx/ui_next/src/screens/User/User.jsx @@ -9,11 +9,10 @@ import { useRouteMatch, useLocation, } from 'react-router-dom'; -import { Card, CardActions, PageSection } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import { Card, PageSection } from '@patternfly/react-core'; import useRequest from '../../util/useRequest'; import { UsersAPI } from '../../api'; -import { TabbedCardHeader } from '../../components/Card'; -import CardCloseButton from '../../components/CardCloseButton'; import ContentError from '../../components/ContentError'; import ContentLoading from '../../components/ContentLoading'; import RoutedTabs from '../../components/RoutedTabs'; @@ -52,6 +51,16 @@ function User({ i18n, setBreadcrumb }) { }, [user, setBreadcrumb]); const tabsArray = [ + { + name: ( + <> + <CaretLeftIcon /> + {i18n._(t`Back to Users`)} + </> + ), + link: `/users`, + id: 99, + }, { name: i18n._(t`Details`), link: `${match.url}/details`, id: 0 }, { name: i18n._(t`Organizations`), @@ -63,6 +72,11 @@ function User({ i18n, setBreadcrumb }) { { name: i18n._(t`Tokens`), link: `${match.url}/tokens`, id: 4 }, ]; + let showCardHeader = true; + if (['edit'].some(name => location.pathname.includes(name))) { + showCardHeader = false; + } + if (contentError) { return ( <PageSection> @@ -82,14 +96,7 @@ function User({ i18n, setBreadcrumb }) { return ( <PageSection> <Card> - {['edit'].some(name => location.pathname.includes(name)) ? null : ( - <TabbedCardHeader> - <RoutedTabs tabsArray={tabsArray} /> - <CardActions> - <CardCloseButton linkTo={userListUrl} /> - </CardActions> - </TabbedCardHeader> - )} + {showCardHeader && <RoutedTabs tabsArray={tabsArray} />} {isLoading && <ContentLoading />} {!isLoading && user && ( <Switch> diff --git a/awx/ui_next/src/screens/User/User.test.jsx b/awx/ui_next/src/screens/User/User.test.jsx index 725247776d..ae3d951ceb 100644 --- a/awx/ui_next/src/screens/User/User.test.jsx +++ b/awx/ui_next/src/screens/User/User.test.jsx @@ -72,20 +72,10 @@ describe('<User />', () => { }, }); }); - await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 5); + await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 6); /* eslint-disable react/button-has-type */ - expect( - wrapper - .find('Tabs') - .containsAllMatchingElements([ - <button aria-label="Details">Details</button>, - <button aria-label="Organizations">Organizations</button>, - <button aria-label="Teams">Teams</button>, - <button aria-label="Access">Access</button>, - <button aria-label="Tokens">Tokens</button>, - ]) - ).toEqual(true); + expect(wrapper.find('Tabs TabButton').length).toEqual(6); }); test('should show content error when user attempts to navigate to erroneous route', async () => { diff --git a/awx/ui_next/src/screens/User/UserAccess/UserAccessListItem.jsx b/awx/ui_next/src/screens/User/UserAccess/UserAccessListItem.jsx index c06e4cca8d..524d07d438 100644 --- a/awx/ui_next/src/screens/User/UserAccess/UserAccessListItem.jsx +++ b/awx/ui_next/src/screens/User/UserAccess/UserAccessListItem.jsx @@ -40,12 +40,12 @@ function UserAccessListItem({ role, i18n, detailUrl, onSelect }) { label={i18n._(t`Role`)} value={ <Chip - isReadOnly={ - !role.summary_fields.user_capabilities.unattach - } key={role.name} aria-label={role.name} onClick={() => onSelect(role)} + isReadOnly={ + !role.summary_fields.user_capabilities.unattach + } > {role.name} </Chip> diff --git a/awx/ui_next/src/screens/User/shared/UserForm.jsx b/awx/ui_next/src/screens/User/shared/UserForm.jsx index 47294ff105..aa5229f296 100644 --- a/awx/ui_next/src/screens/User/shared/UserForm.jsx +++ b/awx/ui_next/src/screens/User/shared/UserForm.jsx @@ -117,7 +117,9 @@ function UserFormFields({ user, i18n }) { fieldId="user-type" helperTextInvalid={userTypeMeta.error} isRequired - isValid={!userTypeMeta.touched || !userTypeMeta.error} + validated={ + !userTypeMeta.touched || !userTypeMeta.error ? 'default' : 'error' + } label={i18n._(t`User Type`)} > <AnsibleSelect |