sentryProjectSelectorField.tsx 2.9 KB

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