schemaHintsDrawer.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import {Fragment, useCallback, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Tag as Badge} from 'sentry/components/core/badge/tag';
  4. import MultipleCheckbox from 'sentry/components/forms/controls/multipleCheckbox';
  5. import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/components';
  6. import {IconSearch} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import type {Tag} from 'sentry/types/group';
  10. import {prettifyTagKey} from 'sentry/utils/discover/fields';
  11. import {FieldKind, FieldValueType, getFieldDefinition} from 'sentry/utils/fields';
  12. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  13. import {
  14. useExploreQuery,
  15. useSetExploreQuery,
  16. } from 'sentry/views/explore/contexts/pageParamsContext';
  17. type SchemaHintsDrawerProps = {
  18. hints: Tag[];
  19. };
  20. function SchemaHintsDrawer({hints}: SchemaHintsDrawerProps) {
  21. const exploreQuery = useExploreQuery();
  22. const setExploreQuery = useSetExploreQuery();
  23. const selectedFilterKeys = useMemo(() => {
  24. const filterQuery = new MutableSearch(exploreQuery);
  25. return filterQuery.getFilterKeys();
  26. }, [exploreQuery]);
  27. const sortedHints = useMemo(() => {
  28. return hints.toSorted((a, b) => {
  29. // may need to fix this if we don't want to ignore the prefix
  30. const aWithoutPrefix = prettifyTagKey(a.key).replace(/^_/, '');
  31. const bWithoutPrefix = prettifyTagKey(b.key).replace(/^_/, '');
  32. return aWithoutPrefix.localeCompare(bWithoutPrefix);
  33. });
  34. }, [hints]);
  35. const handleCheckboxChange = useCallback(
  36. (hint: Tag) => {
  37. const filterQuery = new MutableSearch(exploreQuery);
  38. if (filterQuery.getFilterKeys().includes(hint.key)) {
  39. filterQuery.removeFilter(hint.key);
  40. } else {
  41. const hintFieldDefinition = getFieldDefinition(hint.key, 'span', hint.kind);
  42. filterQuery.addFilterValue(
  43. hint.key,
  44. hintFieldDefinition?.valueType === FieldValueType.BOOLEAN
  45. ? 'True'
  46. : hint.kind === FieldKind.MEASUREMENT
  47. ? '>0'
  48. : ''
  49. );
  50. }
  51. setExploreQuery(filterQuery.formatString());
  52. },
  53. [exploreQuery, setExploreQuery]
  54. );
  55. return (
  56. <Fragment>
  57. <DrawerHeader hideBar />
  58. <DrawerBody>
  59. <HeaderContainer>
  60. <SchemaHintsHeader>{t('Filter Attributes')}</SchemaHintsHeader>
  61. <IconSearch size="md" />
  62. </HeaderContainer>
  63. <StyledMultipleCheckbox name={t('Filter keys')} value={selectedFilterKeys}>
  64. {sortedHints.map(hint => {
  65. const hintFieldDefinition = getFieldDefinition(hint.key, 'span', hint.kind);
  66. const hintType =
  67. hintFieldDefinition?.valueType === FieldValueType.BOOLEAN
  68. ? t('boolean')
  69. : hint.kind === FieldKind.MEASUREMENT
  70. ? t('number')
  71. : t('string');
  72. return (
  73. <StyledMultipleCheckboxItem
  74. key={hint.key}
  75. value={hint.key}
  76. onChange={() => handleCheckboxChange(hint)}
  77. >
  78. <CheckboxLabelContainer>
  79. <CheckboxLabel>{prettifyTagKey(hint.key)}</CheckboxLabel>
  80. <Badge>{hintType}</Badge>
  81. </CheckboxLabelContainer>
  82. </StyledMultipleCheckboxItem>
  83. );
  84. })}
  85. </StyledMultipleCheckbox>
  86. </DrawerBody>
  87. </Fragment>
  88. );
  89. }
  90. export default SchemaHintsDrawer;
  91. const SchemaHintsHeader = styled('h4')`
  92. margin: 0;
  93. `;
  94. const HeaderContainer = styled('div')`
  95. display: flex;
  96. justify-content: space-between;
  97. align-items: center;
  98. margin-bottom: ${space(2)};
  99. `;
  100. const CheckboxLabelContainer = styled('div')`
  101. display: flex;
  102. align-items: center;
  103. justify-content: space-between;
  104. width: 100%;
  105. gap: ${space(1)};
  106. padding-right: ${space(0.5)};
  107. `;
  108. const CheckboxLabel = styled('span')`
  109. font-weight: ${p => p.theme.fontWeightNormal};
  110. margin: 0;
  111. ${p => p.theme.overflowEllipsis};
  112. `;
  113. const StyledMultipleCheckbox = styled(MultipleCheckbox)`
  114. flex-direction: column;
  115. `;
  116. const StyledMultipleCheckboxItem = styled(MultipleCheckbox.Item)`
  117. width: 100%;
  118. padding: ${space(1)} 0;
  119. border-top: 1px solid ${p => p.theme.border};
  120. @media (min-width: ${p => p.theme.breakpoints.small}) {
  121. width: 100%;
  122. }
  123. &:last-child {
  124. border-bottom: 1px solid ${p => p.theme.border};
  125. }
  126. & > label {
  127. width: 100%;
  128. margin: 0;
  129. display: flex;
  130. }
  131. & > label > span {
  132. width: 100%;
  133. ${p => p.theme.overflowEllipsis};
  134. }
  135. `;