conditions.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import Button from 'sentry/components/button';
  4. import FieldRequiredBadge from 'sentry/components/forms/field/fieldRequiredBadge';
  5. import TextareaField from 'sentry/components/forms/textareaField';
  6. import {IconDelete} from 'sentry/icons/iconDelete';
  7. import {t} from 'sentry/locale';
  8. import space from 'sentry/styles/space';
  9. import {SamplingInnerName} from 'sentry/types/sampling';
  10. import {getInnerNameLabel} from '../../utils';
  11. import {TagValueAutocomplete, TagValueAutocompleteProps} from './tagValueAutocomplete';
  12. import {getMatchFieldPlaceholder, getTagKey} from './utils';
  13. export type Condition = {
  14. category: SamplingInnerName;
  15. match?: string;
  16. };
  17. interface Props extends Pick<TagValueAutocompleteProps, 'orgSlug' | 'projectId'> {
  18. conditions: Condition[];
  19. onChange: <T extends keyof Condition>(
  20. index: number,
  21. field: T,
  22. value: Condition[T]
  23. ) => void;
  24. onDelete: (index: number) => void;
  25. }
  26. export function Conditions({conditions, orgSlug, projectId, onDelete, onChange}: Props) {
  27. return (
  28. <Fragment>
  29. {conditions.map((condition, index) => {
  30. const {category, match} = condition;
  31. const isAutoCompleteField =
  32. category === SamplingInnerName.TRACE_ENVIRONMENT ||
  33. category === SamplingInnerName.TRACE_RELEASE;
  34. return (
  35. <ConditionWrapper key={index}>
  36. <LeftCell>
  37. <span>
  38. {getInnerNameLabel(category)}
  39. <FieldRequiredBadge />
  40. </span>
  41. </LeftCell>
  42. <CenterCell>
  43. {isAutoCompleteField ? (
  44. <TagValueAutocomplete
  45. category={category}
  46. tagKey={getTagKey(condition)}
  47. orgSlug={orgSlug}
  48. projectId={projectId}
  49. value={match}
  50. onChange={value => onChange(index, 'match', value)}
  51. />
  52. ) : (
  53. <StyledTextareaField
  54. name="match"
  55. value={match}
  56. onChange={value => onChange(index, 'match', value)}
  57. placeholder={getMatchFieldPlaceholder(category)}
  58. inline={false}
  59. rows={1}
  60. autosize
  61. hideControlState
  62. flexibleControlStateSize
  63. required
  64. stacked
  65. />
  66. )}
  67. </CenterCell>
  68. <RightCell>
  69. <Button
  70. onClick={() => onDelete(index)}
  71. icon={<IconDelete />}
  72. aria-label={t('Delete Condition')}
  73. />
  74. </RightCell>
  75. </ConditionWrapper>
  76. );
  77. })}
  78. </Fragment>
  79. );
  80. }
  81. const ConditionWrapper = styled('div')`
  82. display: grid;
  83. grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  84. align-items: flex-start;
  85. padding: ${space(1)} ${space(2)};
  86. :not(:last-child) {
  87. border-bottom: 1px solid ${p => p.theme.gray100};
  88. }
  89. @media (min-width: ${p => p.theme.breakpoints.small}) {
  90. grid-template-columns: minmax(0, 0.6fr) minmax(0, 1fr) max-content;
  91. }
  92. `;
  93. const Cell = styled('div')`
  94. min-height: 40px;
  95. display: inline-flex;
  96. align-items: center;
  97. `;
  98. const LeftCell = styled(Cell)`
  99. padding-right: ${space(1)};
  100. line-height: 16px;
  101. `;
  102. const CenterCell = styled(Cell)`
  103. padding-top: ${space(1)};
  104. grid-column: 1/-1;
  105. grid-row: 2/2;
  106. ${p => !p.children && 'display: none'};
  107. @media (min-width: ${p => p.theme.breakpoints.small}) {
  108. grid-column: auto;
  109. grid-row: auto;
  110. padding-top: 0;
  111. }
  112. `;
  113. const RightCell = styled(Cell)`
  114. justify-content: flex-end;
  115. padding-left: ${space(1)};
  116. `;
  117. const StyledTextareaField = styled(TextareaField)`
  118. padding-bottom: 0;
  119. width: 100%;
  120. `;