queries.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import * as React from 'react';
  2. import styled from '@emotion/styled';
  3. import Button from 'app/components/button';
  4. import SearchBar from 'app/components/events/searchBar';
  5. import {IconAdd, IconDelete} from 'app/icons';
  6. import {t} from 'app/locale';
  7. import space from 'app/styles/space';
  8. import {Organization} from 'app/types';
  9. import Input from 'app/views/settings/components/forms/controls/input';
  10. import Field from 'app/views/settings/components/forms/field';
  11. import {WidgetQuery} from '../../types';
  12. import {DisplayType} from '../utils';
  13. type Props = {
  14. queries: WidgetQuery[];
  15. selectedProjectIds: number[];
  16. organization: Organization;
  17. displayType: DisplayType;
  18. onRemoveQuery: (index: number) => void;
  19. onAddQuery: () => void;
  20. onChangeQuery: (queryIndex: number, queries: WidgetQuery) => void;
  21. errors?: Array<Record<string, any>>;
  22. };
  23. function Queries({
  24. queries,
  25. selectedProjectIds,
  26. organization,
  27. displayType,
  28. onRemoveQuery,
  29. onAddQuery,
  30. onChangeQuery,
  31. errors,
  32. }: Props) {
  33. function handleFieldChange(queryIndex: number, field: keyof WidgetQuery) {
  34. const widgetQuery = queries[queryIndex];
  35. return function handleChange(value: string) {
  36. const newQuery = {...widgetQuery, [field]: value};
  37. onChangeQuery(queryIndex, newQuery);
  38. };
  39. }
  40. function canAddNewQuery() {
  41. const rightDisplayType = [
  42. DisplayType.LINE,
  43. DisplayType.AREA,
  44. DisplayType.STACKED_AREA,
  45. DisplayType.BAR,
  46. ].includes(displayType);
  47. const underQueryLimit = queries.length < 3;
  48. return rightDisplayType && underQueryLimit;
  49. }
  50. const hideLegendAlias = [
  51. DisplayType.TABLE,
  52. DisplayType.WORLD_MAP,
  53. DisplayType.BIG_NUMBER,
  54. ].includes(displayType);
  55. return (
  56. <div>
  57. {queries.map((query, queryIndex) => {
  58. const displayDeleteButton = queries.length > 1;
  59. const displayLegendAlias = !hideLegendAlias;
  60. return (
  61. <StyledField
  62. key={queryIndex}
  63. inline={false}
  64. flexibleControlStateSize
  65. stacked
  66. error={errors?.[queryIndex].conditions}
  67. >
  68. <Fields
  69. displayDeleteButton={displayDeleteButton}
  70. displayLegendAlias={displayLegendAlias}
  71. >
  72. <SearchBar
  73. organization={organization}
  74. projectIds={selectedProjectIds}
  75. query={query.conditions}
  76. fields={[]}
  77. onSearch={handleFieldChange(queryIndex, 'conditions')}
  78. onBlur={handleFieldChange(queryIndex, 'conditions')}
  79. useFormWrapper={false}
  80. />
  81. {displayLegendAlias && (
  82. <Input
  83. type="text"
  84. name="name"
  85. required
  86. value={query.name}
  87. placeholder={t('Legend Alias')}
  88. onChange={event =>
  89. handleFieldChange(queryIndex, 'name')(event.target.value)
  90. }
  91. />
  92. )}
  93. {displayDeleteButton && (
  94. <Button
  95. size="zero"
  96. borderless
  97. onClick={event => {
  98. event.preventDefault();
  99. onRemoveQuery(queryIndex);
  100. }}
  101. icon={<IconDelete />}
  102. title={t('Remove query')}
  103. label={t('Remove query')}
  104. />
  105. )}
  106. </Fields>
  107. </StyledField>
  108. );
  109. })}
  110. {canAddNewQuery() && (
  111. <Button
  112. size="small"
  113. icon={<IconAdd isCircled />}
  114. onClick={(event: React.MouseEvent) => {
  115. event.preventDefault();
  116. onAddQuery();
  117. }}
  118. >
  119. {t('Add Query')}
  120. </Button>
  121. )}
  122. </div>
  123. );
  124. }
  125. export default Queries;
  126. const fieldsColumns = (p: {
  127. displayDeleteButton: boolean;
  128. displayLegendAlias: boolean;
  129. }) => {
  130. if (!p.displayDeleteButton && !p.displayLegendAlias) {
  131. return '1fr';
  132. }
  133. if (!p.displayDeleteButton) {
  134. return '1fr 33%';
  135. }
  136. if (!p.displayLegendAlias) {
  137. return '1fr max-content';
  138. }
  139. return '1fr 33% max-content';
  140. };
  141. const Fields = styled('div')<{displayDeleteButton: boolean; displayLegendAlias: boolean}>`
  142. display: grid;
  143. grid-template-columns: ${fieldsColumns};
  144. grid-gap: ${space(1)};
  145. align-items: center;
  146. `;
  147. const StyledField = styled(Field)`
  148. padding-right: 0;
  149. `;