From e6475f21f6d4dfb25cbf4f73aa48600a992341f8 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Fri, 30 Aug 2019 16:31:03 -0700 Subject: [PATCH] flush out JT form; upgrade enzyme; add CheckboxField --- awx/ui_next/package-lock.json | 221 ++++++++++++------ awx/ui_next/package.json | 4 +- .../AnsibleSelect/AnsibleSelect.jsx | 22 +- .../components/FormField/CheckboxField.jsx | 54 +++++ awx/ui_next/src/components/FormField/index.js | 1 + .../components/MultiSelect/TagMultiSelect.jsx | 10 +- .../MultiSelect/TagMultiSelect.test.jsx | 34 +++ .../Template/shared/JobTemplateForm.jsx | 162 ++++++++++--- 8 files changed, 388 insertions(+), 120 deletions(-) create mode 100644 awx/ui_next/src/components/FormField/CheckboxField.jsx create mode 100644 awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx diff --git a/awx/ui_next/package-lock.json b/awx/ui_next/package-lock.json index 79dae38600..db64b4ef90 100644 --- a/awx/ui_next/package-lock.json +++ b/awx/ui_next/package-lock.json @@ -2072,9 +2072,9 @@ "dev": true }, "@types/node": { - "version": "11.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.4.tgz", - "integrity": "sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ==", + "version": "12.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", + "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==", "dev": true }, "@types/stack-utils": { @@ -2338,13 +2338,13 @@ "integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==" }, "airbnb-prop-types": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz", - "integrity": "sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz", + "integrity": "sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA==", "dev": true, "requires": { - "array.prototype.find": "^2.0.4", - "function.prototype.name": "^1.1.0", + "array.prototype.find": "^2.1.0", + "function.prototype.name": "^1.1.1", "has": "^1.0.3", "is-regex": "^1.0.4", "object-is": "^1.0.1", @@ -2352,9 +2352,21 @@ "object.entries": "^1.1.0", "prop-types": "^15.7.2", "prop-types-exact": "^1.2.0", - "react-is": "^16.8.6" + "react-is": "^16.9.0" }, "dependencies": { + "function.prototype.name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.1.tgz", + "integrity": "sha512-e1NzkiJuw6xqVH7YSdiW/qDHebcmMhPNe6w+4ZYYEg0VA+LaLzx37RimbPLuonHhYGFGPx1ME2nSi74JiaCr/Q==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1", + "functions-have-names": "^1.1.1", + "is-callable": "^1.1.4" + } + }, "object.entries": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", @@ -2379,9 +2391,9 @@ } }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", "dev": true } } @@ -2949,13 +2961,35 @@ "dev": true }, "array.prototype.find": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", - "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.0.tgz", + "integrity": "sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } } }, "array.prototype.flat": { @@ -5183,7 +5217,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -5886,9 +5920,9 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "enzyme": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.9.0.tgz", - "integrity": "sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.10.0.tgz", + "integrity": "sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg==", "dev": true, "requires": { "array.prototype.flat": "^1.2.1", @@ -5915,18 +5949,19 @@ } }, "enzyme-adapter-react-16": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.12.1.tgz", - "integrity": "sha512-GB61gvY97XvrA6qljExGY+lgI6BBwz+ASLaRKct9VQ3ozu0EraqcNn3CcrUckSGIqFGa1+CxO5gj5is5t3lwrw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz", + "integrity": "sha512-7PcOF7pb4hJUvjY7oAuPGpq3BmlCig3kxXGi2kFx0YzJHppqX1K8IIV9skT1IirxXlu8W7bneKi+oQ10QRnhcA==", "dev": true, "requires": { - "enzyme-adapter-utils": "^1.11.0", + "enzyme-adapter-utils": "^1.12.0", + "has": "^1.0.3", "object.assign": "^4.1.0", "object.values": "^1.1.0", "prop-types": "^15.7.2", "react-is": "^16.8.6", "react-test-renderer": "^16.0.0-0", - "semver": "^5.6.0" + "semver": "^5.7.0" }, "dependencies": { "prop-types": { @@ -5941,20 +5976,26 @@ } }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, "enzyme-adapter-utils": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.11.0.tgz", - "integrity": "sha512-0VZeoE9MNx+QjTfsjmO1Mo+lMfunucYB4wt5ficU85WB/LoetTJrbuujmHP3PJx6pSoaAuLA+Mq877x4LoxdNg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz", + "integrity": "sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA==", "dev": true, "requires": { - "airbnb-prop-types": "^2.12.0", + "airbnb-prop-types": "^2.13.2", "function.prototype.name": "^1.1.0", "object.assign": "^4.1.0", "object.fromentries": "^2.0.0", @@ -5974,9 +6015,9 @@ } }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", "dev": true } } @@ -7940,6 +7981,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.1.1.tgz", + "integrity": "sha512-U0kNHUoxwPNPWOJaMG7Z00d4a/qZVrFtzWJRaK8V9goaVOCXBSQSJpt3MYGNtkScKEBKovxLjnNdC9MlXwo5Pw==", + "dev": true + }, "fuzzaldrin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", @@ -8348,9 +8395,9 @@ } }, "html-element-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.0.1.tgz", - "integrity": "sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.1.0.tgz", + "integrity": "sha512-iqiG3dTZmy+uUaTmHarTL+3/A2VW9ox/9uasKEZC+R/wAtUrTcRlXPSaPqsnWPfIu8wqn09jQNwMRqzL54jSYA==", "dev": true, "requires": { "array-filter": "^1.0.0" @@ -8396,9 +8443,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -8406,13 +8453,19 @@ "util-deprecate": "^1.0.1" } }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } } } @@ -11809,9 +11862,9 @@ "dev": true }, "nearley": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz", - "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.18.0.tgz", + "integrity": "sha512-/zQOMCeJcioI0xJtd5RpBiWw2WP7wLe6vq8/3Yu0rEwgus/G/+pViX80oA87JdVgjRt2895mZSv2VfZmy4W1uw==", "dev": true, "requires": { "commander": "^2.19.0", @@ -13376,32 +13429,22 @@ } }, "react-test-renderer": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", - "integrity": "sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.9.0.tgz", + "integrity": "sha512-R62stB73qZyhrJo7wmCW9jgl/07ai+YzvouvCXIJLBkRlRqLx4j9RqcLEAfNfU3OxTGucqR2Whmn3/Aad6L3hQ==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.13.6" + "react-is": "^16.9.0", + "scheduler": "^0.15.0" }, "dependencies": { "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==", "dev": true - }, - "scheduler": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", - "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } } } }, @@ -14364,6 +14407,16 @@ "object-assign": "^4.1.1" } }, + "scheduler": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", + "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -15250,14 +15303,36 @@ } }, "string.prototype.trim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", - "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz", + "integrity": "sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.0", - "function-bind": "^1.0.2" + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0", + "function-bind": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } } }, "string_decoder": { diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json index 18236a002d..44f3e2926d 100644 --- a/awx/ui_next/package.json +++ b/awx/ui_next/package.json @@ -33,8 +33,8 @@ "babel-plugin-macros": "^2.4.2", "babel-plugin-styled-components": "^1.10.0", "css-loader": "^1.0.0", - "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.12.1", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.14.0", "enzyme-to-json": "^3.3.5", "eslint": "^5.6.0", "eslint-config-airbnb": "^17.1.0", diff --git a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx index d5ea358915..1de791ab58 100644 --- a/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx +++ b/awx/ui_next/src/components/AnsibleSelect/AnsibleSelect.jsx @@ -1,5 +1,13 @@ import React from 'react'; -import PropTypes from 'prop-types'; +import { + arrayOf, + oneOfType, + func, + number, + string, + shape, + bool, +} from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { FormSelect, FormSelectOption } from '@patternfly/react-core'; @@ -48,12 +56,12 @@ AnsibleSelect.defaultProps = { }; AnsibleSelect.propTypes = { - data: PropTypes.arrayOf(PropTypes.object), - id: PropTypes.string.isRequired, - isValid: PropTypes.bool, - onBlur: PropTypes.func, - onChange: PropTypes.func.isRequired, - value: PropTypes.string.isRequired, + data: arrayOf(shape()), + id: string.isRequired, + isValid: bool, + onBlur: func, + onChange: func.isRequired, + value: oneOfType([string, number]).isRequired, }; export { AnsibleSelect as _AnsibleSelect }; diff --git a/awx/ui_next/src/components/FormField/CheckboxField.jsx b/awx/ui_next/src/components/FormField/CheckboxField.jsx new file mode 100644 index 0000000000..5d702a38f9 --- /dev/null +++ b/awx/ui_next/src/components/FormField/CheckboxField.jsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { string, func } from 'prop-types'; +import { Field } from 'formik'; +import { Checkbox, Tooltip } from '@patternfly/react-core'; +import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons'; +import styled from 'styled-components'; + +const QuestionCircleIcon = styled(PFQuestionCircleIcon)` + margin-left: 10px; +`; + +function CheckboxField({ id, name, label, tooltip, validate, ...rest }) { + return ( + ( + + {label} +   + {tooltip && ( + + + + )} + + } + id={id} + {...rest} + checked={field.value} + {...field} + onChange={(value, event) => { + field.onChange(event); + }} + /> + )} + /> + ); +} +CheckboxField.propTypes = { + id: string.isRequired, + name: string.isRequired, + label: string.isRequired, + validate: func, + tooltip: string, +}; +CheckboxField.defaultProps = { + validate: () => {}, + tooltip: '', +}; + +export default CheckboxField; diff --git a/awx/ui_next/src/components/FormField/index.js b/awx/ui_next/src/components/FormField/index.js index 06dabb2d71..c592b7c800 100644 --- a/awx/ui_next/src/components/FormField/index.js +++ b/awx/ui_next/src/components/FormField/index.js @@ -1 +1,2 @@ export { default } from './FormField'; +export { default as CheckboxField } from './CheckboxField'; diff --git a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx index d6d69d46e0..5ebd17e2dc 100644 --- a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx +++ b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.jsx @@ -7,12 +7,16 @@ function arrayToString(tags) { } function stringToArray(value) { - return value.split(',').map(v => ({ - id: v, - name: v, + return value.split(',').filter(val => !!val).map(val => ({ + id: val, + name: val, })); } +/* + * Adapter providing a simplified API to a MultiSelect. The value + * is a comma-separated string. + */ function TagMultiSelect ({ onChange, value }) { const [options, setOptions] = useState(stringToArray(value)); diff --git a/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx new file mode 100644 index 0000000000..3002319283 --- /dev/null +++ b/awx/ui_next/src/components/MultiSelect/TagMultiSelect.test.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import TagMultiSelect from './TagMultiSelect'; + +describe('', () => { + it('should render MultiSelect', () => { + const wrapper = mount( + + ); + expect(wrapper.find('MultiSelect').prop('options')).toEqual([ + { id: 'foo', name: 'foo' }, + { id: 'bar', name: 'bar' }, + ]); + }); + + it('should not treat empty string as an option', () => { + const wrapper = mount(); + expect(wrapper.find('MultiSelect').prop('options')).toEqual([]); + }); + + // NOTE: this test throws a warning which *should* be go away once we upgrade + // to React 16.8 (https://github.com/airbnb/enzyme/blob/master/docs/api/ReactWrapper/invoke.md) + it('should trigger onChange', () => { + const onChange = jest.fn(); + const wrapper = mount( + + ); + + const input = wrapper.find('TextInput'); + input.invoke('onChange')('baz'); + input.invoke('onKeyDown')({ key: 'Tab' }); + expect(onChange).toHaveBeenCalledWith('foo,bar,baz'); + }); +}); diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index bd686bf3cf..b9e9fa8711 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -4,14 +4,21 @@ import { withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { withFormik, Field } from 'formik'; -import { Form, FormGroup, Tooltip, Card, Switch } from '@patternfly/react-core'; +import { + Form, + FormGroup, + Tooltip, + Card, + Switch, + Checkbox, +} from '@patternfly/react-core'; import { QuestionCircleIcon as PFQuestionCircleIcon } from '@patternfly/react-icons'; import ContentError from '@components/ContentError'; import ContentLoading from '@components/ContentLoading'; import AnsibleSelect from '@components/AnsibleSelect'; import MultiSelect, { TagMultiSelect } from '@components/MultiSelect'; import FormActionGroup from '@components/FormActionGroup'; -import FormField from '@components/FormField'; +import FormField, { CheckboxField } from '@components/FormField'; import FormRow from '@components/FormRow'; import CollapsibleSection from '@components/CollapsibleSection'; import { required } from '@util/validators'; @@ -25,6 +32,17 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)` margin-left: 10px; `; +const GridFormGroup = styled(FormGroup)` + & > label { + grid-column: 1 / -1; + } + + && { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + } +`; + class JobTemplateForm extends Component { static propTypes = { template: JobTemplate, @@ -60,6 +78,7 @@ class JobTemplateForm extends Component { inventory: props.template.summary_fields.inventory, relatedProjectPlaybooks: props.relatedProjectPlaybooks, relatedInstanceGroups: [], + allowCallbacks: !!props.template.host_config_key, }; this.handleNewLabel = this.handleNewLabel.bind(this); this.loadLabels = this.loadLabels.bind(this); @@ -119,9 +138,10 @@ class JobTemplateForm extends Component { return; } try { - console.log('loading...'); const { data } = await JobTemplatesAPI.readInstanceGroups(template.id); - console.log(data.results); + this.setState({ + relatedInstanceGroups: data.results, + }); } catch (err) { this.setState({ contentError: err }); } @@ -236,6 +256,7 @@ class JobTemplateForm extends Component { newLabels, removedLabels, relatedInstanceGroups, + allowCallbacks, } = this.state; const { handleCancel, @@ -554,47 +575,110 @@ class JobTemplateForm extends Component { /> { - return ( - - ( + + - - - form.setFieldValue(field.name, value)} - value={field.value} - /> - - ); - }} + > + + + form.setFieldValue(field.name, value)} + /> + + )} /> - - ( + + - - - + + + form.setFieldValue(field.name, value)} + /> + + )} + /> + + + + {i18n._(t`Provisioning Callbacks`)} +   + + + + + } + id="option-callbacks" + checked={allowCallbacks} + onChange={checked => { + this.setState({ allowCallbacks: checked }); + }} + /> + + + + + HERE @@ -621,6 +705,10 @@ const FormikApp = withFormik({ diff_mode, job_tags, skip_tags, + become_enabled, + allow_callbacks, + allow_simultaneous, + use_fact_cache, summary_fields = { labels: { results: [] } }, } = { ...template }; @@ -640,6 +728,10 @@ const FormikApp = withFormik({ diff_mode: diff_mode || false, job_tags: job_tags || '', skip_tags: skip_tags || '', + become_enabled: become_enabled || false, + allow_callbacks: allow_callbacks || false, + allow_simultaneous: allow_simultaneous || false, + use_fact_cache: use_fact_cache || false, }; }, handleSubmit: (values, bag) => bag.props.handleSubmit(values),