columnEditModal.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import {Fragment, useEffect, useState} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import type {ModalRenderProps} from 'sentry/actionCreators/modal';
  5. import {Button} from 'sentry/components/button';
  6. import ButtonBar from 'sentry/components/buttonBar';
  7. import ExternalLink from 'sentry/components/links/externalLink';
  8. import {DISCOVER2_DOCS_URL} from 'sentry/constants';
  9. import {t, tct} from 'sentry/locale';
  10. import {space} from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types';
  12. import {trackAnalytics} from 'sentry/utils/analytics';
  13. import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  14. import type {Column} from 'sentry/utils/discover/fields';
  15. import {FieldKey} from 'sentry/utils/fields';
  16. import theme from 'sentry/utils/theme';
  17. import useTags from 'sentry/utils/useTags';
  18. import {generateFieldOptions} from 'sentry/views/discover/utils';
  19. import ColumnEditCollection from './columnEditCollection';
  20. type Props = {
  21. columns: Column[];
  22. measurementKeys: null | string[];
  23. // Fired when column selections have been applied.
  24. onApply: (columns: Column[]) => void;
  25. organization: Organization;
  26. customMeasurements?: CustomMeasurementCollection;
  27. spanOperationBreakdownKeys?: string[];
  28. } & ModalRenderProps;
  29. function ColumnEditModal(props: Props) {
  30. const {
  31. Header,
  32. Body,
  33. Footer,
  34. measurementKeys,
  35. spanOperationBreakdownKeys,
  36. organization,
  37. onApply,
  38. closeModal,
  39. customMeasurements,
  40. } = props;
  41. // Only run once for each organization.id.
  42. useEffect(() => {
  43. trackAnalytics('discover_v2.column_editor.open', {organization});
  44. }, [organization]);
  45. const tags = useTags();
  46. const tagKeys = Object.keys(tags);
  47. const [columns, setColumns] = useState<Column[]>(props.columns);
  48. function handleApply() {
  49. onApply(columns);
  50. closeModal();
  51. }
  52. const fieldOptions = generateFieldOptions({
  53. organization,
  54. tagKeys,
  55. measurementKeys,
  56. spanOperationBreakdownKeys,
  57. customMeasurements: Object.values(customMeasurements ?? {}).map(
  58. ({key, functions}) => ({
  59. key,
  60. functions,
  61. })
  62. ),
  63. });
  64. return (
  65. <Fragment>
  66. <Header closeButton>
  67. <h4>{t('Edit Columns')}</h4>
  68. </Header>
  69. <Body>
  70. <Instruction>
  71. {tct(
  72. 'To group events, add [functionLink: functions] f(x) that may take in additional parameters. [fieldTagLink: Tag and field] columns will help you view more details about the events (i.e. title).',
  73. {
  74. functionLink: (
  75. <ExternalLink href="https://docs.sentry.io/product/discover-queries/query-builder/#filter-by-table-columns" />
  76. ),
  77. fieldTagLink: (
  78. <ExternalLink href="https://docs.sentry.io/product/sentry-basics/search/searchable-properties/#event-properties" />
  79. ),
  80. }
  81. )}
  82. </Instruction>
  83. <ColumnEditCollection
  84. columns={columns}
  85. fieldOptions={fieldOptions}
  86. filterAggregateParameters={option =>
  87. option.value.meta.name !== FieldKey.TOTAL_COUNT
  88. }
  89. onChange={setColumns}
  90. organization={organization}
  91. />
  92. </Body>
  93. <Footer>
  94. <ButtonBar gap={1}>
  95. <Button priority="default" href={DISCOVER2_DOCS_URL} external>
  96. {t('Read the Docs')}
  97. </Button>
  98. <Button aria-label={t('Apply')} priority="primary" onClick={handleApply}>
  99. {t('Apply')}
  100. </Button>
  101. </ButtonBar>
  102. </Footer>
  103. </Fragment>
  104. );
  105. }
  106. const Instruction = styled('div')`
  107. margin-bottom: ${space(4)};
  108. `;
  109. const modalCss = css`
  110. @media (min-width: ${theme.breakpoints.medium}) {
  111. width: auto;
  112. max-width: 900px;
  113. }
  114. `;
  115. export default ColumnEditModal;
  116. export {modalCss};