sentryProjectSelectorField.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import {Component} from 'react';
  2. import {components} from 'react-select';
  3. import InputField, {InputFieldProps} from 'sentry/components/forms/inputField';
  4. import SelectControl from 'sentry/components/forms/selectControl';
  5. import IdBadge from 'sentry/components/idBadge';
  6. import {t} from 'sentry/locale';
  7. import {Project} from 'sentry/types';
  8. const defaultProps = {
  9. avatarSize: 20,
  10. placeholder: t('Choose Sentry project'),
  11. };
  12. // projects can be passed as a direct prop as well
  13. export interface RenderFieldProps extends InputFieldProps {
  14. projects?: Project[];
  15. }
  16. interface RenderProps
  17. extends Omit<Partial<Readonly<typeof defaultProps>>, 'placeholder'>,
  18. RenderFieldProps {
  19. projects: Project[]; // can't use AvatarProject since we need the ID
  20. }
  21. class RenderField extends Component<RenderProps> {
  22. static defaultProps = defaultProps;
  23. // need to map the option object to the value
  24. handleChange = (
  25. onBlur: RenderFieldProps['onBlur'],
  26. onChange: RenderFieldProps['onChange'],
  27. optionObj: {value: any},
  28. event: React.MouseEvent
  29. ) => {
  30. const {value} = optionObj;
  31. onChange?.(value, event);
  32. onBlur?.(value, event);
  33. };
  34. render() {
  35. const {projects, avatarSize, onChange, onBlur, ...rest} = this.props;
  36. const projectOptions = projects.map(({slug, id}) => ({value: id, label: slug}));
  37. const customOptionProject = projectProps => {
  38. const project = projects.find(proj => proj.id === projectProps.value);
  39. // shouldn't happen but need to account for it
  40. if (!project) {
  41. return <components.Option {...projectProps} />;
  42. }
  43. return (
  44. <components.Option {...projectProps}>
  45. <IdBadge
  46. project={project}
  47. avatarSize={avatarSize}
  48. displayName={project.slug}
  49. avatarProps={{consistentWidth: true}}
  50. />
  51. </components.Option>
  52. );
  53. };
  54. const customValueContainer = containerProps => {
  55. const selectedValue = containerProps.getValue()[0];
  56. const project = projects.find(proj => proj.id === selectedValue?.value);
  57. // shouldn't happen but need to account for it
  58. if (!project) {
  59. return <components.ValueContainer {...containerProps} />;
  60. }
  61. return (
  62. <components.ValueContainer {...containerProps}>
  63. <IdBadge
  64. project={project}
  65. avatarSize={avatarSize}
  66. displayName={project.slug}
  67. avatarProps={{consistentWidth: true}}
  68. />
  69. </components.ValueContainer>
  70. );
  71. };
  72. return (
  73. <SelectControl
  74. options={projectOptions}
  75. components={{
  76. Option: customOptionProject,
  77. SingleValue: customValueContainer,
  78. }}
  79. {...rest}
  80. onChange={this.handleChange.bind(this, onBlur, onChange)}
  81. />
  82. );
  83. }
  84. }
  85. const SentryProjectSelectorField = (props: RenderFieldProps) => (
  86. <InputField
  87. {...props}
  88. field={(renderProps: RenderProps) => <RenderField {...renderProps} />}
  89. />
  90. );
  91. export default SentryProjectSelectorField;