Merge pull request #7305 from jlmitch5/pfv4upgrade

patternfly v4 upgrade

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot] 2020-06-23 14:07:07 +00:00 committed by GitHub
commit a669db989c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
125 changed files with 1335 additions and 1834 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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);

View File

@ -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);

View File

@ -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}
>

View File

@ -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

View File

@ -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>
);
}
}

View File

@ -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={[

View File

@ -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>
);

View File

@ -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);
});
});

View File

@ -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>
);

View File

@ -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;

View File

@ -1,3 +1,2 @@
export { default as TabbedCardHeader } from './TabbedCardHeader';
export { default as CardBody } from './CardBody';
export { default as CardActionsRow } from './CardActionsRow';

View File

@ -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);

View File

@ -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');
});
});

View File

@ -1 +0,0 @@
export { default } from './CardCloseButton';

View File

@ -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}

View File

@ -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

View File

@ -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>

View File

@ -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}

View File

@ -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>
);

View File

@ -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>

View File

@ -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`);

View File

@ -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>
);
}
}

View File

@ -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';

View File

@ -39,7 +39,7 @@ describe('ErrorDetail', () => {
}
/>
);
wrapper.find('Expandable').prop('onToggle')();
wrapper.find('ExpandableSection').prop('onToggle')();
wrapper.update();
});
});

View File

@ -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}

View File

@ -14,7 +14,7 @@ function PasswordField(props) {
fieldId={id}
helperTextInvalid={meta.error}
isRequired={isRequired}
isValid={isValid}
validated={isValid ? 'default' : 'error'}
label={label}
>
<InputGroup>

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 => (

View File

@ -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({

View File

@ -84,7 +84,7 @@ function CredentialLookup({
<FormGroup
fieldId="credential"
isRequired={required}
isValid={isValid}
validated={isValid ? 'default' : 'error'}
label={label}
helperTextInvalid={helperTextInvalid}
>

View File

@ -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={[

View File

@ -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')();
});

View File

@ -49,7 +49,7 @@ function OrganizationLookup({
fieldId="organization"
helperTextInvalid={helperTextInvalid}
isRequired={required}
isValid={isValid}
validated={isValid ? 'default' : 'error'}
label={i18n._(t`Organization`)}
>
<Lookup

View File

@ -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} />}

View File

@ -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>

View File

@ -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);
});

View File

@ -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>

View File

@ -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;
}
`;

View File

@ -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={

View File

@ -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')();

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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`}

View File

@ -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} />

View File

@ -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"]')

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>
);
}
}

View File

@ -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);

View File

@ -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 () =>
@ -89,17 +90,19 @@ describe('<UserAndTeamAccessAdd/>', () => {
wrapper.find('Button[type="submit"]').prop('onClick')()
);
wrapper.update();
expect(
wrapper.find('WizardNavItem[text="Add resource type"]').prop('isDisabled')
).toBe(false);
expect(
wrapper
.find('WizardNavItem[text="Select items from list"]')
.find('WizardNavItem[content="Add resource type"]')
.prop('isDisabled')
).toBe(false);
expect(
wrapper
.find('WizardNavItem[text="Select roles to apply"]')
.find('WizardNavItem[content="Select items from list"]')
.prop('isDisabled')
).toBe(false);
expect(
wrapper
.find('WizardNavItem[content="Select roles to apply"]')
.prop('isDisabled')
).toBe(true);
});

View File

@ -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;
}
`;

View File

@ -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"]')

View File

@ -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"]')

View File

@ -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>

View File

@ -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"

View File

@ -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 () => {

View File

@ -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

View File

@ -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.'
);
});

View File

@ -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 =>

View File

@ -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>

View File

@ -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';
@ -47,6 +45,16 @@ function Host({ i18n, setBreadcrumb }) {
}, [match.params.id, location, setBreadcrumb]);
const tabsArray = [
{
name: (
<>
<CaretLeftIcon />
{i18n._(t`Back to Hosts`)}
</>
),
link: `/hosts`,
id: 99,
},
{
name: i18n._(t`Details`),
link: `${match.url}/details`,
@ -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 && [

View File

@ -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>

View File

@ -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"

View File

@ -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 () => {

View File

@ -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"

View File

@ -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')();
});

View File

@ -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 />}

View File

@ -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 />}

View File

@ -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"

View File

@ -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 () => {

View File

@ -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>
{
{

View File

@ -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`)}

View File

@ -229,7 +229,7 @@ export const VerbosityField = withI18n()(({ i18n }) => {
return (
<FormGroup
fieldId="verbosity"
isValid={isValid}
validated={isValid ? 'default' : 'error'}
label={i18n._(t`Verbosity`)}
>
<FieldTooltip

View File

@ -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>

View File

@ -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"

View File

@ -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 ? (

View File

@ -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', () => {

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
);
let showCardHeader = true;
if (!isInitialized) {
cardHeader = null;
}
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"

View File

@ -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();

View File

@ -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>

View File

@ -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 && (

View File

@ -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();

View File

@ -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>
{
{

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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 && (

Some files were not shown because too many files have changed in this diff Show More