ValuesForm.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import React, { Component } from "react";
  2. import styled from "styled-components";
  3. import {
  4. Section,
  5. FormElement,
  6. ShowIf,
  7. ShowIfOr,
  8. ShowIfAnd,
  9. ShowIfNot,
  10. } from "shared/types";
  11. import { Context } from "shared/Context";
  12. import CheckboxRow from "./CheckboxRow";
  13. import InputRow from "./InputRow";
  14. import Base64InputRow from "./Base64InputRow";
  15. import SelectRow from "./SelectRow";
  16. import Helper from "./Helper";
  17. import Heading from "./Heading";
  18. import ExpandableResource from "../ExpandableResource";
  19. import ServiceRow from "./ServiceRow";
  20. import VeleroForm from "../forms/VeleroForm";
  21. import InputArray from "./InputArray";
  22. import KeyValueArray from "./KeyValueArray";
  23. type PropsType = {
  24. sections?: Section[];
  25. metaState?: any;
  26. setMetaState?: (key: string, value: any) => void;
  27. handleEnvChange?: (x: any) => void;
  28. disabled?: boolean;
  29. externalValues?: any;
  30. };
  31. type StateType = any;
  32. // Requires an internal representation unlike other values components because metaState value underdetermines input order
  33. export default class ValuesForm extends Component<PropsType, StateType> {
  34. getInputValue = (item: FormElement) => {
  35. if (item) {
  36. let key = item.name || item.variable;
  37. let value = this.props.metaState[key]?.value;
  38. if (
  39. item.settings &&
  40. item.settings.unit &&
  41. value &&
  42. value.includes &&
  43. !item.settings.omitUnitFromValue
  44. ) {
  45. value = value.split(item.settings.unit)[0];
  46. }
  47. return value;
  48. }
  49. };
  50. renderSection = (section: Section) => {
  51. return section.contents?.map((item: FormElement, i: number) => {
  52. if (!item) {
  53. return;
  54. }
  55. // If no name is assigned use values.yaml variable as identifier
  56. let key = item.name || item.variable;
  57. let isDisabled =
  58. item.settings?.disableAfterLaunch &&
  59. !this.props.externalValues?.isLaunch;
  60. isDisabled = isDisabled || this.props.disabled;
  61. switch (item.type) {
  62. case "heading":
  63. return (
  64. <Heading key={i} docs={item.settings?.docs}>
  65. {item.label}
  66. </Heading>
  67. );
  68. case "subtitle":
  69. return <Helper key={i}>{item.label}</Helper>;
  70. case "service-ip-list":
  71. if (Array.isArray(item.value)) {
  72. return (
  73. <ResourceList key={key}>
  74. {item.value?.map((service: any, i: number) => {
  75. return <ServiceRow service={service} key={i} />;
  76. })}
  77. </ResourceList>
  78. );
  79. }
  80. case "resource-list":
  81. if (Array.isArray(item.value)) {
  82. return (
  83. <ResourceList key={key}>
  84. {item.value?.map((resource: any, i: number) => {
  85. if (resource.data) {
  86. return (
  87. <ExpandableResource
  88. key={i}
  89. resource={resource}
  90. isLast={i === item.value.length - 1}
  91. roundAllCorners={true}
  92. />
  93. );
  94. }
  95. })}
  96. </ResourceList>
  97. );
  98. }
  99. case "checkbox":
  100. return (
  101. <CheckboxRow
  102. key={key}
  103. disabled={isDisabled}
  104. isRequired={item.required}
  105. checked={this.props.metaState[key]?.value}
  106. toggle={() =>
  107. this.props.setMetaState(key, !this.props.metaState[key]?.value)
  108. }
  109. label={item.label}
  110. />
  111. );
  112. case "env-key-value-array":
  113. return (
  114. <KeyValueArray
  115. key={key}
  116. width="100%"
  117. envLoader={true}
  118. externalValues={this.props.externalValues}
  119. values={this.props.metaState[key]?.value}
  120. setValues={(x: any) => {
  121. this.props.setMetaState(key, x);
  122. // Need to pull env vars out of form.yaml for createGHA build env vars
  123. if (
  124. this.props.handleEnvChange &&
  125. key === "container.env.normal"
  126. ) {
  127. // this.props.handleEnvChange(x);
  128. }
  129. }}
  130. label={item.label}
  131. disabled={isDisabled}
  132. secretOption={true}
  133. />
  134. );
  135. case "key-value-array":
  136. return (
  137. <KeyValueArray
  138. key={key}
  139. width="100%"
  140. externalValues={this.props.externalValues}
  141. values={this.props.metaState[key]?.value}
  142. setValues={(x: any) => this.props.setMetaState(key, x)}
  143. label={item.label}
  144. disabled={isDisabled}
  145. />
  146. );
  147. case "array-input":
  148. return (
  149. <InputArray
  150. key={key}
  151. width="100%"
  152. values={this.props.metaState[key]?.value}
  153. setValues={(x: string[]) => {
  154. this.props.setMetaState(key, x);
  155. }}
  156. label={item.label}
  157. disabled={isDisabled}
  158. />
  159. );
  160. case "string-input":
  161. return (
  162. <InputRow
  163. key={key}
  164. width="100%"
  165. placeholder={item.placeholder}
  166. isRequired={item.required}
  167. type="text"
  168. value={this.getInputValue(item)}
  169. setValue={(x: string) => {
  170. if (
  171. item.settings &&
  172. item.settings.unit &&
  173. x !== "" &&
  174. !item.settings.omitUnitFromValue
  175. ) {
  176. x = x + item.settings.unit;
  177. }
  178. this.props.setMetaState(key, x);
  179. }}
  180. label={item.label}
  181. info={item.info}
  182. unit={item.settings ? item.settings.unit : null}
  183. disabled={isDisabled}
  184. />
  185. );
  186. case "string-input-password":
  187. return (
  188. <InputRow
  189. key={key}
  190. width="100%"
  191. placeholder={item.placeholder}
  192. isRequired={item.required}
  193. type="password"
  194. value={this.getInputValue(item)}
  195. setValue={(x: string) => {
  196. if (
  197. item.settings &&
  198. item.settings.unit &&
  199. x !== "" &&
  200. !item.settings.omitUnitFromValue
  201. ) {
  202. x = x + item.settings.unit;
  203. }
  204. this.props.setMetaState(key, x);
  205. }}
  206. label={item.label}
  207. unit={item.settings ? item.settings.unit : null}
  208. disabled={isDisabled}
  209. />
  210. );
  211. case "number-input":
  212. return (
  213. <InputRow
  214. key={key}
  215. width="100%"
  216. isRequired={item.required}
  217. placeholder={item.placeholder}
  218. type="number"
  219. value={this.getInputValue(item)}
  220. setValue={(x: number) => {
  221. let val: string | number = x;
  222. if (Number.isNaN(x)) {
  223. val = "";
  224. }
  225. // Convert to string if unit is set
  226. if (
  227. item.settings &&
  228. item.settings.unit &&
  229. !item.settings.omitUnitFromValue
  230. ) {
  231. val = x.toString();
  232. val = val + item.settings.unit;
  233. }
  234. this.props.setMetaState(key, val);
  235. }}
  236. label={item.label}
  237. unit={item.settings ? item.settings.unit : null}
  238. disabled={isDisabled}
  239. />
  240. );
  241. case "select":
  242. return (
  243. <SelectRow
  244. key={key}
  245. value={this.props.metaState[key]?.value}
  246. setActiveValue={(val) => this.props.setMetaState(key, val)}
  247. options={item.settings.options}
  248. dropdownLabel=""
  249. label={item.label}
  250. />
  251. );
  252. case "provider-select":
  253. return (
  254. <SelectRow
  255. key={key}
  256. value={this.props.metaState[key]?.value}
  257. setActiveValue={(val) => this.props.setMetaState(key, val)}
  258. options={[
  259. { value: "aws", label: "Amazon Web Services (AWS)" },
  260. { value: "gcp", label: "Google Cloud Platform (GCP)" },
  261. { value: "do", label: "DigitalOcean" },
  262. ]}
  263. dropdownLabel=""
  264. label={item.label}
  265. />
  266. );
  267. case "velero-create-backup":
  268. return <VeleroForm />;
  269. case "base-64":
  270. return (
  271. <Base64InputRow
  272. key={key}
  273. width="100%"
  274. isRequired={item.required}
  275. type="text"
  276. value={this.getInputValue(item)}
  277. setValue={(x: string) => {
  278. if (
  279. item.settings &&
  280. item.settings.unit &&
  281. x !== "" &&
  282. !item.settings.omitUnitFromValue
  283. ) {
  284. x = x + item.settings.unit;
  285. }
  286. this.props.setMetaState(key, btoa(x));
  287. }}
  288. label={item.label}
  289. unit={item.settings ? item.settings.unit : null}
  290. disabled={isDisabled}
  291. />
  292. );
  293. case "base-64-password":
  294. return (
  295. <Base64InputRow
  296. key={key}
  297. isRequired={item.required}
  298. type="password"
  299. value={this.getInputValue(item)}
  300. setValue={(x: string) => {
  301. if (
  302. item.settings &&
  303. item.settings.unit &&
  304. x !== "" &&
  305. !item.settings.omitUnitFromValue
  306. ) {
  307. x = x + item.settings.unit;
  308. }
  309. this.props.setMetaState(key, btoa(x));
  310. }}
  311. label={item.label}
  312. unit={item.settings ? item.settings.unit : null}
  313. disabled={isDisabled}
  314. />
  315. );
  316. default:
  317. }
  318. });
  319. };
  320. evalShowIf = (vals: ShowIf): boolean => {
  321. if (!vals) {
  322. return false;
  323. }
  324. if (typeof vals == "string") {
  325. return !!this.props.metaState[vals]?.value;
  326. }
  327. if ((vals as ShowIfOr).or) {
  328. vals = vals as ShowIfOr;
  329. for (let i = 0; i < vals.or.length; i++) {
  330. if (this.evalShowIf(vals.or[i])) {
  331. return true;
  332. }
  333. }
  334. return false;
  335. }
  336. if ((vals as ShowIfAnd).and) {
  337. vals = vals as ShowIfAnd;
  338. for (let i = 0; i < vals.and.length; i++) {
  339. if (!this.evalShowIf(vals.and[i])) {
  340. return false;
  341. }
  342. }
  343. return true;
  344. }
  345. if ((vals as ShowIfNot).not) {
  346. vals = vals as ShowIfNot;
  347. return !this.evalShowIf(vals.not);
  348. }
  349. return false;
  350. };
  351. renderFormContents = () => {
  352. if (this.props.metaState) {
  353. return this.props.sections?.map((section: Section, i: number) => {
  354. // Hide collapsible section if deciding field is false
  355. if (section.show_if && !this.evalShowIf(section.show_if)) {
  356. return null;
  357. }
  358. return <div key={i}>{this.renderSection(section)}</div>;
  359. });
  360. }
  361. };
  362. render() {
  363. return (
  364. <StyledValuesForm>
  365. <DarkMatter />
  366. {this.renderFormContents()}
  367. </StyledValuesForm>
  368. );
  369. }
  370. }
  371. ValuesForm.contextType = Context;
  372. const ResourceList = styled.div`
  373. margin-bottom: 15px;
  374. margin-top: 20px;
  375. border-radius: 5px;
  376. overflow: hidden;
  377. `;
  378. const DarkMatter = styled.div`
  379. margin-top: 0px;
  380. `;
  381. const StyledValuesForm = styled.div`
  382. width: 100%;
  383. height: 100%;
  384. background: #ffffff11;
  385. color: #ffffff;
  386. padding: 0px 35px 25px;
  387. position: relative;
  388. border-radius: 5px;
  389. font-size: 13px;
  390. overflow: auto;
  391. `;