helpSearch.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. function HelpSearch(props: Props) {
  43. return (
  44. <Search
  45. {...props}
  46. sources={[HelpSource]}
  47. minSearch={3}
  48. closeOnSelect={false}
  49. renderItem={renderResult}
  50. />
  51. );
  52. }
  53. const SectionHeading = styled('div')`
  54. display: grid;
  55. grid-template-columns: max-content 1fr max-content;
  56. gap: ${space(1)};
  57. align-items: center;
  58. background: ${p => p.theme.backgroundSecondary};
  59. padding: ${space(1)} ${space(2)};
  60. &:not(:first-of-type) {
  61. border-top: 1px solid ${p => p.theme.innerBorder};
  62. }
  63. `;
  64. const Count = styled('div')`
  65. font-size: ${p => p.theme.fontSizeSmall};
  66. color: ${p => p.theme.gray300};
  67. `;
  68. const Empty = styled('div')`
  69. display: flex;
  70. align-items: center;
  71. padding: ${space(2)};
  72. color: ${p => p.theme.subText};
  73. font-size: ${p => p.theme.fontSizeMedium};
  74. border-top: 1px solid ${p => p.theme.innerBorder};
  75. `;
  76. export default HelpSearch;