index.tsx 4.3 KB

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