selectAsyncField.tsx 3.8 KB

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