index.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import styled from '@emotion/styled';
  2. import ButtonBar from 'sentry/components/buttonBar';
  3. import FieldGroup from 'sentry/components/forms/fieldGroup';
  4. import {t} from 'sentry/locale';
  5. import {space} from 'sentry/styles/space';
  6. import type {TagCollection} from 'sentry/types';
  7. import type {QueryFieldValue} from 'sentry/utils/discover/fields';
  8. import useCustomMeasurements from 'sentry/utils/useCustomMeasurements';
  9. import useOrganization from 'sentry/utils/useOrganization';
  10. import {getDatasetConfig} from 'sentry/views/dashboards/datasetConfig/base';
  11. import type {Widget} from 'sentry/views/dashboards/types';
  12. import {DisplayType} from 'sentry/views/dashboards/types';
  13. import {QueryField} from 'sentry/views/discover/table/queryField';
  14. import {FieldValueKind} from 'sentry/views/discover/table/types';
  15. import {AddButton} from './addButton';
  16. import {DeleteButton} from './deleteButton';
  17. interface Props {
  18. aggregates: QueryFieldValue[];
  19. displayType: DisplayType;
  20. /**
  21. * Fired when aggregates are added/removed/modified/reordered.
  22. */
  23. onChange: (aggregates: QueryFieldValue[]) => void;
  24. tags: TagCollection;
  25. widgetType: Widget['widgetType'];
  26. errors?: Record<string, any>;
  27. noFieldsMessage?: string;
  28. }
  29. export function YAxisSelector({
  30. displayType,
  31. widgetType,
  32. aggregates,
  33. tags,
  34. onChange,
  35. errors,
  36. noFieldsMessage,
  37. }: Props) {
  38. const organization = useOrganization();
  39. const datasetConfig = getDatasetConfig(widgetType);
  40. const {customMeasurements} = useCustomMeasurements();
  41. function handleAddOverlay(event: React.MouseEvent) {
  42. event.preventDefault();
  43. const newAggregates = [
  44. ...aggregates,
  45. {kind: FieldValueKind.FIELD, field: ''} as QueryFieldValue,
  46. ];
  47. onChange(newAggregates);
  48. }
  49. function handleAddEquation(event: React.MouseEvent) {
  50. event.preventDefault();
  51. const newAggregates = [
  52. ...aggregates,
  53. {kind: FieldValueKind.EQUATION, field: ''} as QueryFieldValue,
  54. ];
  55. onChange(newAggregates);
  56. }
  57. function handleRemoveQueryField(event: React.MouseEvent, fieldIndex: number) {
  58. event.preventDefault();
  59. const newAggregates = [...aggregates];
  60. newAggregates.splice(fieldIndex, 1);
  61. onChange(newAggregates);
  62. }
  63. function handleChangeQueryField(value: QueryFieldValue, fieldIndex: number) {
  64. const newAggregates = [...aggregates];
  65. newAggregates[fieldIndex] = value;
  66. onChange(newAggregates);
  67. }
  68. const fieldError = errors?.find(error => error?.aggregates)?.aggregates;
  69. const canDelete = aggregates.length > 1;
  70. const hideAddYAxisButtons =
  71. (DisplayType.BIG_NUMBER === displayType && aggregates.length === 1) ||
  72. ([DisplayType.LINE, DisplayType.AREA, DisplayType.BAR].includes(displayType) &&
  73. aggregates.length === 3);
  74. return (
  75. <FieldGroup inline={false} flexibleControlStateSize error={fieldError} stacked>
  76. {aggregates.map((fieldValue, i) => (
  77. <QueryFieldWrapper key={`${fieldValue}:${i}`}>
  78. <QueryField
  79. fieldValue={fieldValue}
  80. fieldOptions={datasetConfig.getTableFieldOptions(
  81. organization,
  82. tags,
  83. customMeasurements
  84. )}
  85. onChange={value => handleChangeQueryField(value, i)}
  86. filterPrimaryOptions={datasetConfig.filterYAxisOptions?.(displayType)}
  87. filterAggregateParameters={datasetConfig.filterYAxisAggregateParams?.(
  88. fieldValue,
  89. displayType
  90. )}
  91. otherColumns={aggregates}
  92. noFieldsMessage={noFieldsMessage}
  93. />
  94. {aggregates.length > 1 &&
  95. (canDelete || fieldValue.kind === FieldValueKind.EQUATION) && (
  96. <DeleteButton onDelete={event => handleRemoveQueryField(event, i)} />
  97. )}
  98. </QueryFieldWrapper>
  99. ))}
  100. {!hideAddYAxisButtons && (
  101. <Actions gap={1}>
  102. <AddButton title={t('Add Overlay')} onAdd={handleAddOverlay} />
  103. {datasetConfig.enableEquations && (
  104. <AddButton title={t('Add an Equation')} onAdd={handleAddEquation} />
  105. )}
  106. </Actions>
  107. )}
  108. </FieldGroup>
  109. );
  110. }
  111. const QueryFieldWrapper = styled('div')`
  112. display: flex;
  113. align-items: center;
  114. justify-content: space-between;
  115. :not(:last-child) {
  116. margin-bottom: ${space(1)};
  117. }
  118. > * + * {
  119. margin-left: ${space(1)};
  120. }
  121. `;
  122. const Actions = styled(ButtonBar)`
  123. justify-content: flex-start;
  124. `;