index.tsx 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import {Component} from 'react';
  2. import flatten from 'lodash/flatten';
  3. import type {Fuse} from 'sentry/utils/fuzzySearch';
  4. import {Result} from './types';
  5. type ChildProps = {
  6. hasAnyResults: boolean;
  7. isLoading: boolean;
  8. results: Result[];
  9. };
  10. type Props = {
  11. children: (props: ChildProps) => React.ReactElement;
  12. params: {orgId: string};
  13. query: string;
  14. sources: React.ComponentType[];
  15. searchOptions?: Fuse.IFuseOptions<any>;
  16. };
  17. type SourceResult = {
  18. isLoading: boolean;
  19. results: Result[];
  20. };
  21. class SearchSources extends Component<Props> {
  22. // `allSources` will be an array of all result objects from each source
  23. renderResults(allSources: SourceResult[]) {
  24. const {children} = this.props;
  25. // loading means if any result has `isLoading` OR any result is null
  26. const isLoading = !!allSources.find(arg => arg.isLoading || arg.results === null);
  27. const foundResults = isLoading
  28. ? []
  29. : flatten(allSources.map(({results}) => results || [])).sort(
  30. (a, b) => (a.score ?? 0) - (b.score ?? 0)
  31. );
  32. const hasAnyResults = !!foundResults.length;
  33. return children({
  34. isLoading,
  35. results: foundResults,
  36. hasAnyResults,
  37. });
  38. }
  39. renderSources(sources: Props['sources'], results: SourceResult[], idx: number) {
  40. if (idx >= sources.length) {
  41. return this.renderResults(results);
  42. }
  43. const Source = sources[idx];
  44. return (
  45. <Source {...this.props}>
  46. {(args: SourceResult) => {
  47. // Mutate the array instead of pushing because we don't know how often
  48. // this child function will be called and pushing will cause duplicate
  49. // results to be pushed for all calls down the chain.
  50. results[idx] = args;
  51. return this.renderSources(sources, results, idx + 1);
  52. }}
  53. </Source>
  54. );
  55. }
  56. render() {
  57. return this.renderSources(
  58. this.props.sources,
  59. new Array(this.props.sources.length),
  60. 0
  61. );
  62. }
  63. }
  64. export default SearchSources;