helpSearch.tsx 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Search} from 'sentry/components/search';
  4. import SearchResult from 'sentry/components/search/searchResult';
  5. import SearchResultWrapper from 'sentry/components/search/searchResultWrapper';
  6. import HelpSource from 'sentry/components/search/sources/helpSource';
  7. import {IconWindow} from 'sentry/icons';
  8. import {t, tn} from 'sentry/locale';
  9. import space from 'sentry/styles/space';
  10. type ItemRenderer = React.ComponentProps<typeof Search>['renderItem'];
  11. const renderResult: ItemRenderer = ({item, matches, itemProps, highlighted}) => {
  12. const sectionHeading =
  13. item.sectionHeading !== undefined ? (
  14. <SectionHeading>
  15. <IconWindow />
  16. {t('From %s', item.sectionHeading)}
  17. <Count>{tn('%s result', '%s results', item.sectionCount ?? 0)}</Count>
  18. </SectionHeading>
  19. ) : null;
  20. if (item.empty) {
  21. return (
  22. <Fragment>
  23. {sectionHeading}
  24. <Empty>{t('No results from %s', item.sectionHeading)}</Empty>
  25. </Fragment>
  26. );
  27. }
  28. return (
  29. <Fragment>
  30. {sectionHeading}
  31. <SearchResultWrapper {...itemProps} highlighted={highlighted}>
  32. <SearchResult highlighted={highlighted} item={item} matches={matches} />
  33. </SearchResultWrapper>
  34. </Fragment>
  35. );
  36. };
  37. type Props = Omit<
  38. React.ComponentProps<typeof Search>,
  39. 'sources' | 'minSearch' | 'closeOnSelect' | 'renderItem'
  40. >;
  41. // TODO(ts): Type based on Search props once that has types
  42. const HelpSearch = (props: Props) => (
  43. <Search
  44. {...props}
  45. sources={[HelpSource]}
  46. minSearch={3}
  47. closeOnSelect={false}
  48. renderItem={renderResult}
  49. />
  50. );
  51. const SectionHeading = styled('div')`
  52. display: grid;
  53. grid-template-columns: max-content 1fr max-content;
  54. gap: ${space(1)};
  55. align-items: center;
  56. background: ${p => p.theme.backgroundSecondary};
  57. padding: ${space(1)} ${space(2)};
  58. &:not(:first-of-type) {
  59. border-top: 1px solid ${p => p.theme.innerBorder};
  60. }
  61. `;
  62. const Count = styled('div')`
  63. font-size: ${p => p.theme.fontSizeSmall};
  64. color: ${p => p.theme.gray300};
  65. `;
  66. const Empty = styled('div')`
  67. display: flex;
  68. align-items: center;
  69. padding: ${space(2)};
  70. color: ${p => p.theme.subText};
  71. font-size: ${p => p.theme.fontSizeMedium};
  72. border-top: 1px solid ${p => p.theme.innerBorder};
  73. `;
  74. export default HelpSearch;