tokenizedQueryGrid.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import {useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {AriaGridListOptions} from '@react-aria/gridlist';
  4. import {Item} from '@react-stately/collections';
  5. import {useListState} from '@react-stately/list';
  6. import type {CollectionChildren} from '@react-types/shared';
  7. import {useSearchQueryBuilder} from 'sentry/components/searchQueryBuilder/context';
  8. import {SearchQueryBuilderFilter} from 'sentry/components/searchQueryBuilder/filter';
  9. import {SearchQueryBuilderInput} from 'sentry/components/searchQueryBuilder/input';
  10. import {SearchQueryBuilderParen} from 'sentry/components/searchQueryBuilder/paren';
  11. import {useQueryBuilderGrid} from 'sentry/components/searchQueryBuilder/useQueryBuilderGrid';
  12. import {makeTokenKey} from 'sentry/components/searchQueryBuilder/utils';
  13. import {type ParseResultToken, Token} from 'sentry/components/searchSyntax/parser';
  14. import {t} from 'sentry/locale';
  15. import {space} from 'sentry/styles/space';
  16. interface TokenizedQueryGridProps {
  17. label?: string;
  18. }
  19. interface GridProps extends AriaGridListOptions<ParseResultToken> {
  20. children: CollectionChildren<ParseResultToken>;
  21. items: ParseResultToken[];
  22. }
  23. function Grid(props: GridProps) {
  24. const ref = useRef<HTMLDivElement>(null);
  25. const state = useListState<ParseResultToken>(props);
  26. const {gridProps} = useQueryBuilderGrid(props, state, ref);
  27. return (
  28. <SearchQueryGridWrapper {...gridProps} ref={ref}>
  29. {[...state.collection].map(item => {
  30. const token = item.value;
  31. switch (token?.type) {
  32. case Token.FILTER:
  33. return (
  34. <SearchQueryBuilderFilter
  35. key={item.key}
  36. token={token}
  37. item={item}
  38. state={state}
  39. />
  40. );
  41. case Token.FREE_TEXT:
  42. case Token.SPACES:
  43. return (
  44. <SearchQueryBuilderInput
  45. key={item.key}
  46. token={token}
  47. item={item}
  48. state={state}
  49. />
  50. );
  51. case Token.L_PAREN:
  52. case Token.R_PAREN:
  53. return (
  54. <SearchQueryBuilderParen
  55. key={item.key}
  56. token={token}
  57. item={item}
  58. state={state}
  59. />
  60. );
  61. // TODO(malwilley): Add other token types
  62. default:
  63. return null;
  64. }
  65. })}
  66. </SearchQueryGridWrapper>
  67. );
  68. }
  69. export function TokenizedQueryGrid({label}: TokenizedQueryGridProps) {
  70. const {parsedQuery} = useSearchQueryBuilder();
  71. // Shouldn't ever get here since we will render the plain text input instead
  72. if (!parsedQuery) {
  73. return null;
  74. }
  75. return (
  76. <Grid aria-label={label ?? t('Create a search query')} items={parsedQuery}>
  77. {item => (
  78. <Item key={makeTokenKey(item, parsedQuery)}>
  79. {item.text.trim() ? item.text : t('Space')}
  80. </Item>
  81. )}
  82. </Grid>
  83. );
  84. }
  85. const SearchQueryGridWrapper = styled('div')`
  86. padding: ${space(0.75)} 48px ${space(0.75)} 32px;
  87. display: flex;
  88. align-items: stretch;
  89. row-gap: ${space(0.5)};
  90. flex-wrap: wrap;
  91. `;