form.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import styled from '@emotion/styled';
  2. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  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 type {Relay} from 'sentry/types/relay';
  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. function 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) => () => {
  46. navigator.clipboard
  47. .writeText(value)
  48. .then(() => {
  49. addSuccessMessage(t('Copied to clipboard'));
  50. })
  51. .catch(() => {
  52. addErrorMessage(t('Error copying to clipboard'));
  53. });
  54. };
  55. return (
  56. <form onSubmit={handleSubmit} id="relay-form">
  57. <FieldGroup
  58. flexibleControlStateSize
  59. label={t('Display Name')}
  60. error={errors.name}
  61. inline={false}
  62. stacked
  63. required
  64. >
  65. <Input
  66. type="text"
  67. name="name"
  68. placeholder={t('Display Name')}
  69. onChange={handleChange('name')}
  70. value={values.name}
  71. onBlur={onValidate('name')}
  72. disabled={disables.name}
  73. />
  74. </FieldGroup>
  75. {disables.publicKey ? (
  76. <FieldGroup
  77. flexibleControlStateSize
  78. label={t('Public Key')}
  79. inline={false}
  80. stacked
  81. >
  82. <TextCopyInput onCopy={onCopy(values.publicKey)}>
  83. {values.publicKey}
  84. </TextCopyInput>
  85. </FieldGroup>
  86. ) : (
  87. <FieldWrapper>
  88. <StyledField
  89. label={t('Public Key')}
  90. error={errors.publicKey}
  91. flexibleControlStateSize
  92. inline={false}
  93. stacked
  94. required
  95. >
  96. <Input
  97. type="text"
  98. name="publicKey"
  99. placeholder={t('Public Key')}
  100. onChange={handleChange('publicKey')}
  101. value={values.publicKey}
  102. onBlur={onValidateKey}
  103. />
  104. </StyledField>
  105. <FieldHelp>
  106. {t(
  107. 'Only enter the Public Key value from your credentials file. Never share the Secret key with Sentry or any third party'
  108. )}
  109. </FieldHelp>
  110. </FieldWrapper>
  111. )}
  112. <FieldGroup
  113. flexibleControlStateSize
  114. label={t('Description')}
  115. inline={false}
  116. stacked
  117. >
  118. <Textarea
  119. name="description"
  120. placeholder={t('Description')}
  121. onChange={handleChange('description')}
  122. value={values.description}
  123. disabled={disables.description}
  124. autosize
  125. />
  126. </FieldGroup>
  127. </form>
  128. );
  129. }
  130. export default Form;
  131. const FieldWrapper = styled('div')`
  132. padding-bottom: ${space(2)};
  133. `;
  134. const StyledField = styled(FieldGroup)`
  135. padding-bottom: 0;
  136. `;