form.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import styled from '@emotion/styled';
  2. import copy from 'copy-text-to-clipboard';
  3. import Textarea from 'sentry/components/forms/controls/textarea';
  4. import FieldGroup from 'sentry/components/forms/fieldGroup';
  5. import FieldHelp from 'sentry/components/forms/fieldGroup/fieldHelp';
  6. import Input from 'sentry/components/input';
  7. import TextCopyInput from 'sentry/components/textCopyInput';
  8. import {t} from 'sentry/locale';
  9. import space from 'sentry/styles/space';
  10. import {Relay} from 'sentry/types';
  11. type FormField = keyof Pick<Relay, 'name' | 'publicKey' | 'description'>;
  12. type Values = Record<FormField, string>;
  13. type Props = {
  14. disables: Partial<Record<FormField, boolean>>;
  15. errors: Partial<Values>;
  16. isFormValid: boolean;
  17. onChange: (field: FormField, value: string) => void;
  18. onSave: () => void;
  19. onValidate: (field: FormField) => () => void;
  20. onValidateKey: () => void;
  21. values: Values;
  22. };
  23. const Form = ({
  24. values,
  25. onChange,
  26. errors,
  27. onValidate,
  28. isFormValid,
  29. disables,
  30. onValidateKey,
  31. onSave,
  32. }: Props) => {
  33. const handleChange =
  34. (field: FormField) =>
  35. (
  36. event: React.ChangeEvent<HTMLTextAreaElement> | React.ChangeEvent<HTMLInputElement>
  37. ) => {
  38. onChange(field, event.target.value);
  39. };
  40. const handleSubmit = () => {
  41. if (isFormValid) {
  42. onSave();
  43. }
  44. };
  45. const onCopy = (value: string) => () => copy(value);
  46. return (
  47. <form onSubmit={handleSubmit} id="relay-form">
  48. <FieldGroup
  49. flexibleControlStateSize
  50. label={t('Display Name')}
  51. error={errors.name}
  52. inline={false}
  53. stacked
  54. required
  55. >
  56. <Input
  57. type="text"
  58. name="name"
  59. placeholder={t('Display Name')}
  60. onChange={handleChange('name')}
  61. value={values.name}
  62. onBlur={onValidate('name')}
  63. disabled={disables.name}
  64. />
  65. </FieldGroup>
  66. {disables.publicKey ? (
  67. <FieldGroup
  68. flexibleControlStateSize
  69. label={t('Public Key')}
  70. inline={false}
  71. stacked
  72. >
  73. <TextCopyInput onCopy={onCopy(values.publicKey)}>
  74. {values.publicKey}
  75. </TextCopyInput>
  76. </FieldGroup>
  77. ) : (
  78. <FieldWrapper>
  79. <StyledField
  80. label={t('Public Key')}
  81. error={errors.publicKey}
  82. flexibleControlStateSize
  83. inline={false}
  84. stacked
  85. required
  86. >
  87. <Input
  88. type="text"
  89. name="publicKey"
  90. placeholder={t('Public Key')}
  91. onChange={handleChange('publicKey')}
  92. value={values.publicKey}
  93. onBlur={onValidateKey}
  94. />
  95. </StyledField>
  96. <FieldHelp>
  97. {t(
  98. 'Only enter the Public Key value from your credentials file. Never share the Secret key with Sentry or any third party'
  99. )}
  100. </FieldHelp>
  101. </FieldWrapper>
  102. )}
  103. <FieldGroup
  104. flexibleControlStateSize
  105. label={t('Description')}
  106. inline={false}
  107. stacked
  108. >
  109. <Textarea
  110. name="description"
  111. placeholder={t('Description')}
  112. onChange={handleChange('description')}
  113. value={values.description}
  114. disabled={disables.description}
  115. autosize
  116. />
  117. </FieldGroup>
  118. </form>
  119. );
  120. };
  121. export default Form;
  122. const FieldWrapper = styled('div')`
  123. padding-bottom: ${space(2)};
  124. `;
  125. const StyledField = styled(FieldGroup)`
  126. padding-bottom: 0;
  127. `;