searchBarAction.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import styled from '@emotion/styled';
  2. import DropdownButtonV2 from 'sentry/components/dropdownButtonV2';
  3. import CompactSelect from 'sentry/components/forms/compactSelect';
  4. import SearchBar from 'sentry/components/searchBar';
  5. import {t, tn} from 'sentry/locale';
  6. import space from 'sentry/styles/space';
  7. type FilterOption = React.ComponentProps<typeof CompactSelect>['options'][0];
  8. type Props = {
  9. onChange: (value: string) => void;
  10. placeholder: string;
  11. query: string;
  12. className?: string;
  13. filterOptions?: FilterOption[];
  14. filterSelections?: FilterOption[];
  15. onFilterChange?: (options: FilterOption[]) => void;
  16. };
  17. function SearchBarAction({
  18. onChange,
  19. query,
  20. placeholder,
  21. filterOptions,
  22. filterSelections,
  23. onFilterChange,
  24. className,
  25. }: Props) {
  26. const trigger: React.ComponentProps<typeof CompactSelect>['trigger'] = ({
  27. props,
  28. ref,
  29. }) => (
  30. <StyledTrigger
  31. size="small"
  32. priority={filterSelections && filterSelections.length > 0 ? 'primary' : 'default'}
  33. ref={ref}
  34. {...props}
  35. >
  36. {filterSelections?.length
  37. ? tn('%s Active Filter', '%s Active Filters', filterSelections.length)
  38. : t('Filter By')}
  39. </StyledTrigger>
  40. );
  41. return (
  42. <Wrapper className={className}>
  43. {filterOptions && (
  44. <CompactSelect
  45. multiple
  46. maxMenuHeight={400}
  47. options={filterOptions}
  48. value={filterSelections?.map(f => f.value)}
  49. onChange={onFilterChange}
  50. trigger={trigger}
  51. />
  52. )}
  53. <StyledSearchBar
  54. onChange={onChange}
  55. query={query}
  56. placeholder={placeholder}
  57. blendWithFilter={!!filterOptions}
  58. />
  59. </Wrapper>
  60. );
  61. }
  62. export default SearchBarAction;
  63. const Wrapper = styled('div')`
  64. display: flex;
  65. width: 100%;
  66. justify-content: flex-end;
  67. @media (max-width: ${p => p.theme.breakpoints.small}) {
  68. margin-top: ${space(1)};
  69. flex-direction: column;
  70. }
  71. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  72. width: 400px;
  73. }
  74. @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
  75. width: 600px;
  76. }
  77. `;
  78. // TODO(matej): remove this once we refactor SearchBar to not use css classes
  79. // - it could accept size as a prop
  80. const StyledSearchBar = styled(SearchBar)<{blendWithFilter?: boolean}>`
  81. width: 100%;
  82. position: relative;
  83. .search-input {
  84. height: 34px;
  85. }
  86. .search-clear-form,
  87. .search-input-icon {
  88. height: 32px;
  89. display: flex;
  90. align-items: center;
  91. }
  92. ${p =>
  93. p.blendWithFilter &&
  94. `
  95. .search-input,
  96. .search-input:focus {
  97. border-radius: ${p.theme.borderRadiusRight};
  98. border-left-width: 0;
  99. }
  100. `}
  101. @media (max-width: ${p => p.theme.breakpoints.small}) {
  102. .search-input,
  103. .search-input:focus {
  104. border-radius: ${p => p.theme.borderRadius};
  105. border-left-width: 1px;
  106. }
  107. }
  108. `;
  109. const StyledTrigger = styled(DropdownButtonV2)`
  110. border-radius: ${p => p.theme.borderRadiusLeft};
  111. @media (max-width: ${p => p.theme.breakpoints.small}) {
  112. border-radius: ${p => p.theme.borderRadius};
  113. margin-bottom: ${space(1)};
  114. }
  115. `;