selectAsyncField.tsx 3.2 KB

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