selectAsyncField.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import {Component} from 'react';
  2. import InputField, {InputFieldProps} from 'sentry/components/forms/inputField';
  3. import SelectAsyncControl, {
  4. Result,
  5. SelectAsyncControlProps,
  6. } from 'sentry/components/forms/selectAsyncControl';
  7. // projects can be passed as a direct prop as well
  8. import {GeneralSelectValue} from 'sentry/components/forms/selectControl';
  9. export interface SelectAsyncFieldProps
  10. extends Omit<InputFieldProps, 'highlighted' | 'visible' | 'required' | 'value'>,
  11. SelectAsyncControlProps {}
  12. type SelectAsyncFieldState = {
  13. results: Result[];
  14. latestSelection?: GeneralSelectValue;
  15. };
  16. class SelectAsyncField extends Component<SelectAsyncFieldProps, SelectAsyncFieldState> {
  17. state: SelectAsyncFieldState = {
  18. results: [],
  19. latestSelection: undefined,
  20. };
  21. componentDidMount() {}
  22. // need to map the option object to the value
  23. // this is essentially the same code from ./selectField handleChange()
  24. handleChange = (
  25. onBlur: SelectAsyncFieldProps['onBlur'],
  26. onChange: SelectAsyncFieldProps['onChange'],
  27. optionObj: GeneralSelectValue,
  28. event: React.MouseEvent
  29. ) => {
  30. let {value} = optionObj;
  31. if (!optionObj) {
  32. value = optionObj;
  33. } else if (this.props.multiple && Array.isArray(optionObj)) {
  34. // List of optionObjs
  35. value = optionObj.map(({value: val}) => val);
  36. } else if (!Array.isArray(optionObj)) {
  37. value = optionObj.value;
  38. }
  39. this.setState({latestSelection: optionObj});
  40. onChange?.(value, event);
  41. onBlur?.(value, event);
  42. };
  43. findValue(propsValue: string): GeneralSelectValue {
  44. const {defaultOptions} = this.props;
  45. const {results, latestSelection} = this.state;
  46. // We don't use defaultOptions if it is undefined or a boolean
  47. const options = typeof defaultOptions === 'object' ? defaultOptions : [];
  48. /**
  49. * The propsValue is the `id` of the object (user, team, etc), and
  50. * react-select expects a full value object: {value: "id", label: "name"}
  51. **/
  52. return (
  53. // When rendering the selected value, first look at the API results...
  54. results.find(({value}) => value === propsValue) ??
  55. // Then at the defaultOptions passed in props...
  56. options?.find(({value}) => value === propsValue) ??
  57. // Then at the latest value selected in the form
  58. (latestSelection as GeneralSelectValue)
  59. );
  60. }
  61. render() {
  62. const {...otherProps} = this.props;
  63. return (
  64. <InputField
  65. {...otherProps}
  66. field={({onChange, onBlur, required: _required, onResults, value, ...props}) => (
  67. <SelectAsyncControl
  68. {...props}
  69. onChange={this.handleChange.bind(this, onBlur, onChange)}
  70. onResults={data => {
  71. const results = onResults(data);
  72. const resultSelection = results.find(result => result.value === value);
  73. this.setState(
  74. resultSelection
  75. ? {
  76. results,
  77. latestSelection: resultSelection,
  78. }
  79. : {results}
  80. );
  81. return results;
  82. }}
  83. onSelectResetsInput
  84. onCloseResetsInput={false}
  85. onBlurResetsInput={false}
  86. value={this.findValue(value)}
  87. />
  88. )}
  89. />
  90. );
  91. }
  92. }
  93. export default SelectAsyncField;