rework org edit form to use Formik

This commit is contained in:
Keith Grant
2019-03-27 10:42:06 -04:00
parent 02cd188c2f
commit 40b2539626
6 changed files with 292 additions and 175 deletions

126
package-lock.json generated
View File

@@ -1657,7 +1657,7 @@
}, },
"ansi-colors": { "ansi-colors": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
"requires": { "requires": {
"ansi-wrap": "^0.1.0" "ansi-wrap": "^0.1.0"
@@ -2185,6 +2185,11 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true "dev": true
}, },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"asn1": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -2567,12 +2572,12 @@
}, },
"babel-plugin-syntax-class-properties": { "babel-plugin-syntax-class-properties": {
"version": "6.13.0", "version": "6.13.0",
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=" "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94="
}, },
"babel-plugin-syntax-flow": { "babel-plugin-syntax-flow": {
"version": "6.18.0", "version": "6.18.0",
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
"integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=" "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0="
}, },
"babel-plugin-syntax-jsx": { "babel-plugin-syntax-jsx": {
@@ -4140,6 +4145,15 @@
"sha.js": "^2.4.8" "sha.js": "^2.4.8"
} }
}, },
"create-react-context": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
"integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
"requires": {
"fbjs": "^0.8.0",
"gud": "^1.0.0"
}
},
"cross-spawn": { "cross-spawn": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -4380,6 +4394,11 @@
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
}, },
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
},
"default-gateway": { "default-gateway": {
"version": "2.7.2", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz",
@@ -4711,7 +4730,7 @@
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@@ -4852,7 +4871,6 @@
"version": "0.1.12", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": { "requires": {
"iconv-lite": "~0.4.13" "iconv-lite": "~0.4.13"
} }
@@ -5686,6 +5704,27 @@
"bser": "^2.0.0" "bser": "^2.0.0"
} }
}, },
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
}
}
},
"fbjs-scripts": { "fbjs-scripts": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz", "resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz",
@@ -5736,7 +5775,7 @@
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@@ -6001,6 +6040,22 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"formik": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/formik/-/formik-1.5.1.tgz",
"integrity": "sha512-FBWGBKQkcCE4d5b5l2fKccD9d1QxNxw/0bQTRvp3EjzA8Bnjmsm9H/Oy0375UA8P3FPmfJkF4cXLLdEqK7fP5A==",
"requires": {
"create-react-context": "^0.2.2",
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^2.5.5",
"lodash": "^4.17.11",
"lodash-es": "^4.17.11",
"prop-types": "^15.6.1",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^1.9.3"
}
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -6831,6 +6886,11 @@
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"dev": true "dev": true
}, },
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
},
"handle-thing": { "handle-thing": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
@@ -8031,8 +8091,7 @@
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
"dev": true
}, },
"is-string": { "is-string": {
"version": "1.0.4", "version": "1.0.4",
@@ -8103,6 +8162,15 @@
} }
} }
}, },
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -9264,7 +9332,7 @@
}, },
"kind-of": { "kind-of": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=" "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ="
}, },
"kleur": { "kleur": {
@@ -9358,6 +9426,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
}, },
"lodash-es": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
},
"lodash.assign": { "lodash.assign": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
@@ -10117,7 +10190,6 @@
"version": "1.6.3", "version": "1.6.3",
"resolved": "http://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=", "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=",
"dev": true,
"requires": { "requires": {
"encoding": "^0.1.11", "encoding": "^0.1.11",
"is-stream": "^1.0.1" "is-stream": "^1.0.1"
@@ -11305,6 +11377,14 @@
"integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==",
"dev": true "dev": true
}, },
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
},
"promise-inflight": { "promise-inflight": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -11554,6 +11634,11 @@
"scheduler": "^0.10.0" "scheduler": "^0.10.0"
} }
}, },
"react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"react-hot-loader": { "react-hot-loader": {
"version": "4.3.11", "version": "4.3.11",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.11.tgz", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.11.tgz",
@@ -13014,8 +13099,7 @@
"setimmediate": { "setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
"dev": true
}, },
"setprototypeof": { "setprototypeof": {
"version": "1.1.0", "version": "1.1.0",
@@ -13939,6 +14023,11 @@
"setimmediate": "^1.0.4" "setimmediate": "^1.0.4"
} }
}, },
"tiny-warning": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz",
"integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q=="
},
"tippy.js": { "tippy.js": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-3.4.1.tgz", "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-3.4.1.tgz",
@@ -14111,8 +14200,7 @@
"tslib": { "tslib": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"dev": true
}, },
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
@@ -14163,6 +14251,11 @@
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
"dev": true "dev": true
}, },
"ua-parser-js": {
"version": "0.7.19",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
"integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
},
"uglify-js": { "uglify-js": {
"version": "3.4.9", "version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
@@ -15562,6 +15655,11 @@
"iconv-lite": "0.4.24" "iconv-lite": "0.4.24"
} }
}, },
"whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
},
"whatwg-mimetype": { "whatwg-mimetype": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz",

View File

@@ -52,6 +52,7 @@
"@patternfly/react-icons": "^3.5.0", "@patternfly/react-icons": "^3.5.0",
"@patternfly/react-tokens": "^2.0.4", "@patternfly/react-tokens": "^2.0.4",
"axios": "^0.18.0", "axios": "^0.18.0",
"formik": "^1.5.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.4.1", "react": "^16.4.1",
"react-dom": "^16.4.1", "react-dom": "^16.4.1",

View File

@@ -16,7 +16,7 @@ class AnsibleSelect extends React.Component {
onSelectChange (val, event) { onSelectChange (val, event) {
const { onChange, name } = this.props; const { onChange, name } = this.props;
event.target.name = name; event.target.name = name;
onChange(val, event); onChange(event, val);
} }
render () { render () {
@@ -24,10 +24,22 @@ class AnsibleSelect extends React.Component {
return ( return (
<I18n> <I18n>
{({ i18n }) => ( {({ i18n }) => (
<FormSelect value={value} onChange={this.onSelectChange} aria-label={i18n._(t`Select Input`)}> <FormSelect
{data.map((datum) => (datum === defaultSelected value={value}
? (<FormSelectOption key="" value="" label={i18n._(t`Use Default ${label}`)} />) : (<FormSelectOption key={datum} value={datum} label={datum} />))) onChange={this.onSelectChange}
} aria-label={i18n._(t`Select Input`)}
>
{data.map((datum) => (
datum === defaultSelected ? (
<FormSelectOption
key=""
value=""
label={i18n._(t`Use Default ${label}`)}
/>
) : (
<FormSelectOption key={datum} value={datum} label={datum} />
)
))}
</FormSelect> </FormSelect>
)} )}
</I18n> </I18n>

View File

@@ -28,10 +28,10 @@ const FormActionGroup = ({ onSubmit, submitDisabled, onCancel }) => (
<ActionGroup style={formActionGroupStyle}> <ActionGroup style={formActionGroupStyle}>
<Toolbar> <Toolbar>
<ToolbarGroup style={buttonGroupStyle}> <ToolbarGroup style={buttonGroupStyle}>
<Button aria-label={i18n._(t`Save`)} variant="primary" onClick={onSubmit} isDisabled={submitDisabled}>{i18n._(t`Save`)}</Button> <Button aria-label={i18n._(t`Save`)} variant="primary" type="submit" onClick={onSubmit} isDisabled={submitDisabled}>{i18n._(t`Save`)}</Button>
</ToolbarGroup> </ToolbarGroup>
<ToolbarGroup> <ToolbarGroup>
<Button aria-label={i18n._(t`Cancel`)} variant="secondary" onClick={onCancel}>{i18n._(t`Cancel`)}</Button> <Button aria-label={i18n._(t`Cancel`)} variant="secondary" type="button" onClick={onCancel}>{i18n._(t`Cancel`)}</Button>
</ToolbarGroup> </ToolbarGroup>
</Toolbar> </Toolbar>
</ActionGroup> </ActionGroup>

View File

@@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field } from 'formik';
import { FormGroup, TextInput } from '@patternfly/react-core';
function FormField (props) {
const { id, name, label, validate, isRequired, ...rest } = props;
return (
<Field
name={name}
validate={validate}
render={({ field, form }) => {
const isValid = !form.touched[field.name] || !form.errors[field.name];
return (
<FormGroup
fieldId={id}
helperTextInvalid={form.errors[field.name]}
isRequired={isRequired}
isValid={isValid}
label={label}
>
<TextInput
id={id}
isRequired={isRequired}
isValid={isValid}
{...rest}
{...field}
onChange={(value, event) => {
field.onChange(event);
}}
/>
</FormGroup>
);
}}
/>
);
}
FormField.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
type: PropTypes.string,
validate: PropTypes.func,
isRequired: PropTypes.bool,
};
FormField.defaultProps = {
type: 'text',
validate: () => {},
isRequired: false,
};
export default FormField;

View File

@@ -1,27 +1,35 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { Formik, Field } from 'formik';
import { I18n, i18nMark } from '@lingui/react'; import { I18n, i18nMark } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
CardBody, CardBody,
Form, Form,
FormGroup, FormGroup,
TextInput,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { ConfigContext } from '../../../../context'; import { ConfigContext } from '../../../../context';
import FormField from '../../../../components/FormField';
import FormActionGroup from '../../../../components/FormActionGroup'; import FormActionGroup from '../../../../components/FormActionGroup';
import AnsibleSelect from '../../../../components/AnsibleSelect'; import AnsibleSelect from '../../../../components/AnsibleSelect';
import InstanceGroupsLookup from '../../components/InstanceGroupsLookup'; import InstanceGroupsLookup from '../../components/InstanceGroupsLookup';
function required (message) {
return value => {
if (!value.trim()) {
return message || i18nMark('This field must not be blank');
}
return undefined;
};
}
class OrganizationEdit extends Component { class OrganizationEdit extends Component {
constructor (props) { constructor (props) {
super(props); super(props);
this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this); this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this);
this.checkValidity = this.checkValidity.bind(this);
this.handleFieldChange = this.handleFieldChange.bind(this);
this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind(this); this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
this.postInstanceGroups = this.postInstanceGroups.bind(this); this.postInstanceGroups = this.postInstanceGroups.bind(this);
@@ -29,48 +37,25 @@ class OrganizationEdit extends Component {
this.handleSuccess = this.handleSuccess.bind(this); this.handleSuccess = this.handleSuccess.bind(this);
this.state = { this.state = {
form: { initialInstanceGroups: [],
name: { instanceGroups: [],
value: '',
isValid: true,
validation: {
required: true
},
helperTextInvalid: i18nMark('This field must not be blank')
},
description: {
value: ''
},
instanceGroups: {
value: [],
initialValue: []
},
custom_virtualenv: {
value: '',
defaultValue: '/venv/ansible/'
}
},
error: '', error: '',
formIsValid: true formIsValid: true,
}; };
} }
async componentDidMount () { async componentDidMount () {
const { organization } = this.props; let instanceGroups;
const { form: formData } = this.state;
formData.name.value = organization.name;
formData.description.value = organization.description;
formData.custom_virtualenv.value = organization.custom_virtualenv;
try { try {
formData.instanceGroups.value = await this.getRelatedInstanceGroups(); instanceGroups = await this.getRelatedInstanceGroups();
formData.instanceGroups.initialValue = [...formData.instanceGroups.value];
} catch (err) { } catch (err) {
this.setState({ error: err }); this.setState({ error: err });
} }
this.setState({ form: formData }); this.setState({
instanceGroups,
initialInstanceGroups: instanceGroups,
});
} }
async getRelatedInstanceGroups () { async getRelatedInstanceGroups () {
@@ -79,56 +64,19 @@ class OrganizationEdit extends Component {
organization: { id } organization: { id }
} = this.props; } = this.props;
const { data } = await api.getOrganizationInstanceGroups(id); const { data } = await api.getOrganizationInstanceGroups(id);
const { results } = data; return data.results;
return results;
} }
checkValidity = (value, validation) => { handleInstanceGroupsChange (instanceGroups) {
const isValid = (validation.required) this.setState({ instanceGroups });
? (value.trim() !== '') : true;
return isValid;
} }
handleFieldChange (val, evt) { async handleSubmit (values) {
const targetName = evt.target.name;
const value = val;
const { form: updatedForm } = this.state;
const updatedFormEl = { ...updatedForm[targetName] };
updatedFormEl.value = value;
updatedForm[targetName] = updatedFormEl;
updatedFormEl.isValid = (updatedFormEl.validation)
? this.checkValidity(updatedFormEl.value, updatedFormEl.validation) : true;
const formIsValid = (updatedFormEl.validation) ? updatedFormEl.isValid : true;
this.setState({ form: updatedForm, formIsValid });
}
handleInstanceGroupsChange (val, targetName) {
const { form: updatedForm } = this.state;
updatedForm[targetName].value = val;
this.setState({ form: updatedForm });
}
async handleSubmit () {
const { api, organization } = this.props; const { api, organization } = this.props;
const { form: { name, description, custom_virtualenv } } = this.state; const { instanceGroups } = this.state;
const formData = { name, description, custom_virtualenv };
const updatedData = {};
Object.keys(formData)
.forEach(formId => {
updatedData[formId] = formData[formId].value;
});
try { try {
await api.updateOrganizationDetails(organization.id, updatedData); await api.updateOrganizationDetails(organization.id, values);
await this.postInstanceGroups(); await this.postInstanceGroups(instanceGroups);
} catch (err) { } catch (err) {
this.setState({ error: err }); this.setState({ error: err });
} finally { } finally {
@@ -146,18 +94,18 @@ class OrganizationEdit extends Component {
history.push(`/organizations/${id}`); history.push(`/organizations/${id}`);
} }
async postInstanceGroups () { async postInstanceGroups (instanceGroups) {
const { api, organization } = this.props; const { api, organization } = this.props;
const { form: { instanceGroups } } = this.state; const { initialInstanceGroups } = this.state;
const url = organization.related.instance_groups; const url = organization.related.instance_groups;
const initialInstanceGroups = instanceGroups.initialValue.map(ig => ig.id); const initialIds = initialInstanceGroups.map(ig => ig.id);
const updatedInstanceGroups = instanceGroups.value.map(ig => ig.id); const updatedIds = instanceGroups.map(ig => ig.id);
const groupsToAssociate = [...updatedInstanceGroups] const groupsToAssociate = [...updatedIds]
.filter(x => !initialInstanceGroups.includes(x)); .filter(x => !initialIds.includes(x));
const groupsToDisassociate = [...initialInstanceGroups] const groupsToDisassociate = [...initialIds]
.filter(x => !updatedInstanceGroups.includes(x)); .filter(x => !updatedIds.includes(x));
try { try {
await Promise.all(groupsToAssociate.map(async id => { await Promise.all(groupsToAssociate.map(async id => {
@@ -172,83 +120,85 @@ class OrganizationEdit extends Component {
} }
render () { render () {
const { api } = this.props; const { api, organization } = this.props;
const { const {
form: { instanceGroups,
name,
description,
instanceGroups,
custom_virtualenv
},
formIsValid, formIsValid,
error error,
} = this.state; } = this.state;
const defaultVenv = '/venv/ansible/';
return ( return (
<CardBody> <CardBody>
<I18n> <I18n>
{({ i18n }) => ( {({ i18n }) => (
<Form autoComplete="off"> <Formik
<div style={{ display: 'grid', gridGap: '20px', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))' }}> initialValues={{
<FormGroup name: organization.name,
fieldId="edit-org-form-name" description: organization.description,
helperTextInvalid={name.helperTextInvalid} custom_virtualenv: organization.custom_virtualenv || '',
isRequired }}
isValid={name.isValid} onSubmit={this.handleSubmit}
label={i18n._(t`Name`)} render={formik => (
> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<TextInput <div
id="edit-org-form-name" style={{
isRequired display: 'grid',
isValid={name.isValid} gridGap: '20px',
name="name" gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))'
onChange={this.handleFieldChange} }}
value={name.value || ''} >
<FormField
id="edit-org-form-name"
name="name"
type="text"
label={i18n._(t`Name`)}
validate={required()}
isRequired
/>
<FormField
id="edit-org-form-description"
name="description"
type="text"
label={i18n._(t`Description`)}
/>
<ConfigContext.Consumer>
{({ custom_virtualenvs }) => (
custom_virtualenvs && custom_virtualenvs.length > 1 && (
<Field
name="custom_virtualenv"
render={({ field }) => (
<FormGroup
fieldId="edit-org-custom-virtualenv"
label={i18n._(t`Ansible Environment`)}
>
<AnsibleSelect
data={custom_virtualenvs}
defaultSelected={defaultVenv}
label={i18n._(t`Ansible Environment`)}
{...field}
/>
</FormGroup>
)}
/>
)
)}
</ConfigContext.Consumer>
</div>
<InstanceGroupsLookup
api={api}
value={instanceGroups}
onChange={this.handleInstanceGroupsChange}
/> />
</FormGroup> <FormActionGroup
<FormGroup onCancel={this.handleCancel}
fieldId="edit-org-form-description" onSubmit={formik.handleSubmit}
label={i18n._(t`Description`)} submitDisabled={!formIsValid}
>
<TextInput
id="edit-org-form-description"
name="description"
onChange={this.handleFieldChange}
value={description.value || ''}
/> />
</FormGroup> { error ? <div>error</div> : '' }
<ConfigContext.Consumer> </Form>
{({ custom_virtualenvs }) => ( )}
custom_virtualenvs && custom_virtualenvs.length > 1 && ( />
<FormGroup
fieldId="edit-org-custom-virtualenv"
label={i18n._(t`Ansible Environment`)}
>
<AnsibleSelect
data={custom_virtualenvs}
defaultSelected={custom_virtualenv.defaultEnv}
label={i18n._(t`Ansible Environment`)}
name="custom_virtualenv"
onChange={this.handleFieldChange}
value={custom_virtualenv.value || ''}
/>
</FormGroup>
)
)}
</ConfigContext.Consumer>
</div>
<InstanceGroupsLookup
api={api}
value={instanceGroups.value}
onChange={this.handleInstanceGroupsChange}
/>
<FormActionGroup
onCancel={this.handleCancel}
onSubmit={this.handleSubmit}
submitDisabled={!formIsValid}
/>
{ error ? <div>error</div> : '' }
</Form>
)} )}
</I18n> </I18n>
</CardBody> </CardBody>