genericField.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import React from 'react';
  2. import BooleanField from 'app/components/forms/booleanField';
  3. import EmailField from 'app/components/forms/emailField';
  4. import FormField from 'app/components/forms/formField';
  5. import NumberField from 'app/components/forms/numberField';
  6. import PasswordField from 'app/components/forms/passwordField';
  7. import SelectAsyncField from 'app/components/forms/selectAsyncField';
  8. import SelectCreatableField from 'app/components/forms/selectCreatableField';
  9. import SelectField from 'app/components/forms/selectField';
  10. import FormState from 'app/components/forms/state';
  11. import TextareaField from 'app/components/forms/textareaField';
  12. import TextField from 'app/components/forms/textField';
  13. import {defined} from 'app/utils';
  14. type FieldType =
  15. | 'secret'
  16. | 'range'
  17. | 'bool'
  18. | 'email'
  19. | 'string'
  20. | 'text'
  21. | 'url'
  22. | 'number'
  23. | 'textarea';
  24. type SelectFieldType = 'select' | 'choice';
  25. type Config = {
  26. required?: boolean;
  27. help?: string;
  28. name: string;
  29. label?: string;
  30. placeholder: string;
  31. default: string;
  32. readonly: boolean;
  33. type: FieldType;
  34. choices: Array<[number | string, number | string]>;
  35. };
  36. type SelectFieldConfig = Omit<Config, 'type' | 'has_autocomplete'> & {
  37. type: SelectFieldType;
  38. has_autocomplete: false;
  39. };
  40. type AsyncSelectFieldConfig = Omit<SelectFieldConfig, 'has_autocomplete'> & {
  41. url: string;
  42. has_autocomplete: true;
  43. };
  44. interface FormData {
  45. [name: string]: string;
  46. }
  47. type Props = {
  48. config: Config | SelectFieldConfig | AsyncSelectFieldConfig;
  49. formData: FormData;
  50. formErrors?: object;
  51. formState: typeof FormState[keyof typeof FormState];
  52. onChange: FormField['props']['onChange'];
  53. };
  54. const GenericField = ({
  55. config,
  56. formData = {},
  57. formErrors = {},
  58. formState,
  59. onChange,
  60. }: Props) => {
  61. const required = defined(config.required) ? config.required : true;
  62. const fieldProps = {
  63. ...config,
  64. value: formData[config.name],
  65. onChange,
  66. label: config.label + (required ? '*' : ''),
  67. placeholder: config.placeholder,
  68. required,
  69. name: config.name,
  70. error: (formErrors || {})[config.name],
  71. defaultValue: config.default,
  72. disabled: config.readonly,
  73. key: config.name,
  74. formState,
  75. help:
  76. defined(config.help) && config.help !== '' ? (
  77. <span dangerouslySetInnerHTML={{__html: config.help}} />
  78. ) : null,
  79. };
  80. switch (config.type) {
  81. case 'secret':
  82. return <PasswordField {...fieldProps} />;
  83. case 'bool':
  84. return <BooleanField {...fieldProps} />;
  85. case 'email':
  86. return <EmailField {...fieldProps} />;
  87. case 'string':
  88. case 'text':
  89. case 'url':
  90. if (fieldProps.choices) {
  91. return <SelectCreatableField deprecatedSelectControl {...fieldProps} />;
  92. }
  93. return <TextField {...fieldProps} />;
  94. case 'number':
  95. return <NumberField {...fieldProps} />;
  96. case 'textarea':
  97. return <TextareaField {...fieldProps} />;
  98. case 'choice':
  99. case 'select':
  100. // the chrome required tip winds up in weird places
  101. // for select elements, so just make it look like
  102. // it's required (with *) and rely on server validation
  103. const {required: _, ...selectProps} = fieldProps;
  104. if (config.has_autocomplete) {
  105. // Redeclaring field props here as config has been narrowed to include the correct options for SelectAsyncField
  106. const selectFieldProps = {
  107. ...config,
  108. ...selectProps,
  109. };
  110. return <SelectAsyncField deprecatedSelectControl {...selectFieldProps} />;
  111. }
  112. return <SelectField deprecatedSelectControl {...selectProps} />;
  113. default:
  114. return null;
  115. }
  116. };
  117. export default GenericField;