Просмотр исходного кода

ref(ui): Remove metric alert builder with gui filters flag #23519

This removes the metric alert builder gui filters flag which shows the user the new metric alert builder:
David Wang 4 лет назад
Родитель
Сommit
e21b190879

+ 0 - 2
src/sentry/conf/server.py

@@ -871,8 +871,6 @@ SENTRY_FEATURES = {
     "organizations:incidents": False,
     # Enable metric aggregate in metric alert rule builder
     "organizations:metric-alert-builder-aggregate": False,
-    # Enable new GUI filters in the metric alert rule builder
-    "organizations:metric-alert-gui-filters": False,
     # Enable integration functionality to create and link groups to issues on
     # external services.
     "organizations:integrations-issue-basic": True,

+ 0 - 1
src/sentry/features/__init__.py

@@ -72,7 +72,6 @@ default_manager.add("organizations:filters-and-sampling", OrganizationFeature)
 default_manager.add("organizations:global-views", OrganizationFeature)  # NOQA
 default_manager.add("organizations:incidents", OrganizationFeature)  # NOQA
 default_manager.add("organizations:metric-alert-builder-aggregate", OrganizationFeature)  # NOQA
-default_manager.add("organizations:metric-alert-gui-filters", OrganizationFeature)  # NOQA
 default_manager.add("organizations:integrations-event-hooks", OrganizationFeature)  # NOQA
 default_manager.add("organizations:integrations-issue-basic", OrganizationFeature)  # NOQA
 default_manager.add("organizations:integrations-issue-sync", OrganizationFeature)  # NOQA

+ 208 - 138
src/sentry/static/sentry/app/views/settings/incidentRules/ruleConditionsForm.tsx

@@ -4,35 +4,39 @@ import styled from '@emotion/styled';
 import {addErrorMessage} from 'app/actionCreators/indicator';
 import {Client} from 'app/api';
 import Feature from 'app/components/acl/feature';
-import {Panel, PanelBody, PanelHeader} from 'app/components/panels';
+import SelectControl from 'app/components/forms/selectControl';
+import List from 'app/components/list';
+import ListItem from 'app/components/list/listItem';
+import {Panel, PanelBody} from 'app/components/panels';
 import Tooltip from 'app/components/tooltip';
 import {t, tct} from 'app/locale';
 import space from 'app/styles/space';
 import {Environment, Organization} from 'app/types';
-import {defined} from 'app/utils';
 import {getDisplayName} from 'app/utils/environment';
 import theme from 'app/utils/theme';
-import {DATA_SOURCE_LABELS} from 'app/views/alerts/utils';
+import {
+  convertDatasetEventTypesToSource,
+  DATA_SOURCE_LABELS,
+  DATA_SOURCE_TO_SET_AND_EVENT_TYPES,
+} from 'app/views/alerts/utils';
 import SearchBar from 'app/views/events/searchBar';
-import RadioGroup from 'app/views/settings/components/forms/controls/radioGroup';
-import FieldLabel from 'app/views/settings/components/forms/field/fieldLabel';
 import FormField from 'app/views/settings/components/forms/formField';
 import SelectField from 'app/views/settings/components/forms/selectField';
 
-import {DATASET_EVENT_TYPE_FILTERS, DEFAULT_AGGREGATE} from './constants';
+import {DEFAULT_AGGREGATE} from './constants';
 import MetricField from './metricField';
-import {Dataset, IncidentRule, TimeWindow} from './types';
+import {Datasource, IncidentRule, TimeWindow} from './types';
 
 const TIME_WINDOW_MAP: Record<TimeWindow, string> = {
-  [TimeWindow.ONE_MINUTE]: t('1 minute'),
-  [TimeWindow.FIVE_MINUTES]: t('5 minutes'),
-  [TimeWindow.TEN_MINUTES]: t('10 minutes'),
-  [TimeWindow.FIFTEEN_MINUTES]: t('15 minutes'),
-  [TimeWindow.THIRTY_MINUTES]: t('30 minutes'),
-  [TimeWindow.ONE_HOUR]: t('1 hour'),
-  [TimeWindow.TWO_HOURS]: t('2 hours'),
-  [TimeWindow.FOUR_HOURS]: t('4 hours'),
-  [TimeWindow.ONE_DAY]: t('24 hours'),
+  [TimeWindow.ONE_MINUTE]: t('1 minute window'),
+  [TimeWindow.FIVE_MINUTES]: t('5 minute window'),
+  [TimeWindow.TEN_MINUTES]: t('10 minute window'),
+  [TimeWindow.FIFTEEN_MINUTES]: t('15 minute window'),
+  [TimeWindow.THIRTY_MINUTES]: t('30 minute window'),
+  [TimeWindow.ONE_HOUR]: t('1 hour window'),
+  [TimeWindow.TWO_HOURS]: t('2 hour window'),
+  [TimeWindow.FOUR_HOURS]: t('4 hour window'),
+  [TimeWindow.ONE_DAY]: t('24 hour window'),
 };
 
 type Props = {
@@ -40,7 +44,7 @@ type Props = {
   organization: Organization;
   projectSlug: string;
   disabled: boolean;
-  thresholdChart: React.ReactNode;
+  thresholdChart: React.ReactElement;
   onFilterSearch: (query: string) => void;
 };
 
@@ -48,7 +52,7 @@ type State = {
   environments: Environment[] | null;
 };
 
-class RuleConditionsForm extends React.PureComponent<Props, State> {
+class RuleConditionsFormWithGuiFilters extends React.PureComponent<Props, State> {
   state: State = {
     environments: null,
   };
@@ -79,15 +83,12 @@ class RuleConditionsForm extends React.PureComponent<Props, State> {
     const {organization, disabled, onFilterSearch} = this.props;
     const {environments} = this.state;
 
-    const environmentList: [IncidentRule['environment'], React.ReactNode][] = defined(
-      environments
-    )
-      ? environments.map((env: Environment) => [env.name, getDisplayName(env)])
-      : [];
+    const environmentList: [IncidentRule['environment'], React.ReactNode][] =
+      environments?.map((env: Environment) => [env.name, getDisplayName(env)]) ?? [];
 
     const anyEnvironmentLabel = (
       <React.Fragment>
-        {t('All Environments')}
+        {t('All')}
         <div className="all-environment-note">
           {tct(
             `This will count events across every environment. For example,
@@ -100,65 +101,141 @@ class RuleConditionsForm extends React.PureComponent<Props, State> {
     );
     environmentList.unshift([null, anyEnvironmentLabel]);
 
+    const formElemBaseStyle = {
+      padding: `${space(0.5)}`,
+      border: 'none',
+    };
+
     return (
-      <React.Fragment>
-        <Feature requireAll features={['organizations:performance-view']}>
-          <StyledPanel>
-            <PanelHeader>{t('Alert Conditions')}</PanelHeader>
-            <PanelBody>
-              <FormField required name="dataset" label="Data source">
-                {({onChange, onBlur, value, model, label}) => (
-                  <RadioGroup
-                    orientInline
-                    disabled={disabled}
-                    value={value}
-                    label={label}
-                    onChange={(id, e) => {
-                      onChange(id, e);
-                      onBlur(id, e);
-                      // Reset the aggregate to the default (which works across
-                      // datatypes), otherwise we may send snuba an invalid query
-                      // (transaction aggregate on events datasource = bad).
-                      model.setValue('aggregate', DEFAULT_AGGREGATE);
-                    }}
-                    choices={[
-                      [Dataset.ERRORS, DATA_SOURCE_LABELS[Dataset.ERRORS]],
-                      [Dataset.TRANSACTIONS, DATA_SOURCE_LABELS[Dataset.TRANSACTIONS]],
-                    ]}
-                  />
-                )}
-              </FormField>
-            </PanelBody>
-          </StyledPanel>
-        </Feature>
+      <Panel>
+        <StyledPanelBody>
+          <StyledList symbol="colored-numeric">
+            <ListItem>{t('Select events')}</ListItem>
+            <FormRow>
+              <SelectField
+                name="environment"
+                placeholder={t('All')}
+                style={{
+                  ...formElemBaseStyle,
+                  minWidth: 250,
+                  flex: 1,
+                }}
+                styles={{
+                  singleValue: (base: any) => ({
+                    ...base,
+                    '.all-environment-note': {display: 'none'},
+                  }),
+                  option: (base: any, state: any) => ({
+                    ...base,
+                    '.all-environment-note': {
+                      ...(!state.isSelected && !state.isFocused
+                        ? {color: theme.gray400}
+                        : {}),
+                      fontSize: theme.fontSizeSmall,
+                    },
+                  }),
+                }}
+                choices={environmentList}
+                isDisabled={disabled || this.state.environments === null}
+                isClearable
+                inline={false}
+                flexibleControlStateSize
+                inFieldLabel={t('Environment: ')}
+              />
+              <Feature requireAll features={['organizations:performance-view']}>
+                <FormField
+                  name="datasource"
+                  inline={false}
+                  style={{
+                    ...formElemBaseStyle,
+                    minWidth: 300,
+                    flex: 2,
+                  }}
+                  flexibleControlStateSize
+                >
+                  {({onChange, onBlur, model}) => {
+                    const formDataset = model.getValue('dataset');
+                    const formEventTypes = model.getValue('eventTypes');
+                    const mappedValue = convertDatasetEventTypesToSource(
+                      formDataset,
+                      formEventTypes
+                    );
+                    return (
+                      <SelectControl
+                        value={mappedValue}
+                        inFieldLabel={t('Data Source: ')}
+                        onChange={optionObj => {
+                          const optionValue = optionObj.value;
+                          onChange(optionValue, {});
+                          onBlur(optionValue, {});
+                          // Reset the aggregate to the default (which works across
+                          // datatypes), otherwise we may send snuba an invalid query
+                          // (transaction aggregate on events datasource = bad).
+                          model.setValue('aggregate', DEFAULT_AGGREGATE);
 
-        <div>
-          {/* Contained in the same div for the css sticky overlay */}
-          {this.props.thresholdChart}
-          <StyledPanel>
-            <PanelHeader>{t('Alert Conditions')}</PanelHeader>
-            <PanelBody>
-              <FormField name="query" inline={false}>
+                          // set the value of the dataset and event type from data source
+                          const {dataset, eventTypes} =
+                            DATA_SOURCE_TO_SET_AND_EVENT_TYPES[optionValue] ?? {};
+                          model.setValue('dataset', dataset);
+                          model.setValue('eventTypes', eventTypes);
+                        }}
+                        options={[
+                          {
+                            label: t('Errors'),
+                            options: [
+                              {
+                                value: Datasource.ERROR_DEFAULT,
+                                label: DATA_SOURCE_LABELS[Datasource.ERROR_DEFAULT],
+                              },
+                              {
+                                value: Datasource.DEFAULT,
+                                label: DATA_SOURCE_LABELS[Datasource.DEFAULT],
+                              },
+                              {
+                                value: Datasource.ERROR,
+                                label: DATA_SOURCE_LABELS[Datasource.ERROR],
+                              },
+                            ],
+                          },
+                          {
+                            label: t('Transactions'),
+                            options: [
+                              {
+                                value: Datasource.TRANSACTION,
+                                label: DATA_SOURCE_LABELS[Datasource.TRANSACTION],
+                              },
+                            ],
+                          },
+                        ]}
+                        isDisabled={disabled}
+                        required
+                      />
+                    );
+                  }}
+                </FormField>
+              </Feature>
+              <FormField
+                name="query"
+                inline={false}
+                style={{
+                  ...formElemBaseStyle,
+                  flex: '6 0 700px',
+                }}
+                flexibleControlStateSize
+              >
                 {({onChange, onBlur, onKeyDown, initialData, model}) => (
                   <SearchContainer>
-                    <SearchLabel>{t('Filter')}</SearchLabel>
                     <StyledSearchBar
                       defaultQuery={initialData?.query ?? ''}
-                      inlineLabel={
-                        <Tooltip
-                          title={t(
-                            'Metric alerts are automatically filtered to your data source'
-                          )}
-                        >
-                          <SearchEventTypeNote>
-                            {DATASET_EVENT_TYPE_FILTERS[model.getValue('dataset')]}
-                          </SearchEventTypeNote>
-                        </Tooltip>
-                      }
                       omitTags={['event.type']}
                       disabled={disabled}
                       useFormWrapper={false}
                       organization={organization}
+                      placeholder={
+                        model.getValue('dataset') === 'events'
+                          ? t('Filter events by level, message, or other properties...')
+                          : t('Filter transactions by URL, tags, and other properties...')
+                      }
                       onChange={onChange}
                       onKeyDown={e => {
                         /**
@@ -184,90 +261,83 @@ class RuleConditionsForm extends React.PureComponent<Props, State> {
                   </SearchContainer>
                 )}
               </FormField>
+            </FormRow>
+            <ListItem>{t('Choose a metric')}</ListItem>
+            <FormRow>
               <MetricField
                 name="aggregate"
-                label={t('Metric')}
+                help={null}
                 organization={organization}
                 disabled={disabled}
-                required
-              />
-              <SelectField
-                name="timeWindow"
-                label={t('Time Window')}
-                help={
-                  <React.Fragment>
-                    <div>{t('The time window over which the Metric is evaluated')}</div>
-                    <div>
-                      {t(
-                        'Note: Triggers are evaluated every minute regardless of this value.'
-                      )}
-                    </div>
-                  </React.Fragment>
-                }
-                choices={Object.entries(TIME_WINDOW_MAP)}
-                required
-                isDisabled={disabled}
-                getValue={value => Number(value)}
-                setValue={value => `${value}`}
-              />
-              <SelectField
-                name="environment"
-                label={t('Environment')}
-                placeholder={t('All Environments')}
-                help={t('Choose which environment events must match')}
-                styles={{
-                  singleValue: (base: any) => ({
-                    ...base,
-                    '.all-environment-note': {display: 'none'},
-                  }),
-                  option: (base: any, state: any) => ({
-                    ...base,
-                    '.all-environment-note': {
-                      ...(!state.isSelected && !state.isFocused
-                        ? {color: theme.gray400}
-                        : {}),
-                      fontSize: theme.fontSizeSmall,
-                    },
-                  }),
+                style={{
+                  ...formElemBaseStyle,
                 }}
-                choices={environmentList}
-                isDisabled={disabled || this.state.environments === null}
-                isClearable
+                inline={false}
+                flexibleControlStateSize
+                columnWidth={250}
+                inFieldLabels
+                required
               />
-            </PanelBody>
-          </StyledPanel>
-        </div>
-      </React.Fragment>
+              <FormRowText>{t('over a')}</FormRowText>
+              <Tooltip
+                title={t('Triggers are evaluated every minute regardless of this value.')}
+              >
+                <SelectField
+                  name="timeWindow"
+                  style={{
+                    ...formElemBaseStyle,
+                    flex: 1,
+                    minWidth: 180,
+                  }}
+                  choices={Object.entries(TIME_WINDOW_MAP)}
+                  required
+                  isDisabled={disabled}
+                  getValue={value => Number(value)}
+                  setValue={value => `${value}`}
+                  inline={false}
+                  flexibleControlStateSize
+                />
+              </Tooltip>
+            </FormRow>
+          </StyledList>
+          {this.props.thresholdChart}
+        </StyledPanelBody>
+      </Panel>
     );
   }
 }
 
-const StyledPanel = styled(Panel)`
-  /* Sticky graph panel cannot have margin-bottom */
-  margin-top: ${space(2)};
+const StyledPanelBody = styled(PanelBody)`
+  ol,
+  h4 {
+    margin-bottom: ${space(1)};
+  }
 `;
 
 const SearchContainer = styled('div')`
   display: flex;
 `;
 
-const SearchLabel = styled(FieldLabel)`
-  align-items: center;
-  margin-right: ${space(1)};
-`;
-
 const StyledSearchBar = styled(SearchBar)`
   flex-grow: 1;
 `;
 
-const SearchEventTypeNote = styled('div')`
-  font: ${p => p.theme.fontSizeExtraSmall} ${p => p.theme.text.familyMono};
-  color: ${p => p.theme.subText};
-  background: ${p => p.theme.backgroundSecondary};
-  border-radius: 2px;
-  padding: ${space(0.5)} ${space(0.75)};
-  margin: 0 ${space(0.5)} 0 ${space(1)};
-  user-select: none;
+const StyledList = styled(List)`
+  padding: ${space(3)} ${space(3)} 0 ${space(3)};
+`;
+
+const FormRow = styled('div')`
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+  flex-wrap: wrap;
+  margin-bottom: ${space(2)};
+`;
+
+const FormRowText = styled('div')`
+  padding: ${space(0.5)};
+  /* Match the height of the select controls */
+  line-height: 36px;
 `;
 
-export default RuleConditionsForm;
+export default RuleConditionsFormWithGuiFilters;

+ 0 - 343
src/sentry/static/sentry/app/views/settings/incidentRules/ruleConditionsFormWithGuiFilters.tsx

@@ -1,343 +0,0 @@
-import React from 'react';
-import styled from '@emotion/styled';
-
-import {addErrorMessage} from 'app/actionCreators/indicator';
-import {Client} from 'app/api';
-import Feature from 'app/components/acl/feature';
-import SelectControl from 'app/components/forms/selectControl';
-import List from 'app/components/list';
-import ListItem from 'app/components/list/listItem';
-import {Panel, PanelBody} from 'app/components/panels';
-import Tooltip from 'app/components/tooltip';
-import {t, tct} from 'app/locale';
-import space from 'app/styles/space';
-import {Environment, Organization} from 'app/types';
-import {getDisplayName} from 'app/utils/environment';
-import theme from 'app/utils/theme';
-import {
-  convertDatasetEventTypesToSource,
-  DATA_SOURCE_LABELS,
-  DATA_SOURCE_TO_SET_AND_EVENT_TYPES,
-} from 'app/views/alerts/utils';
-import SearchBar from 'app/views/events/searchBar';
-import FormField from 'app/views/settings/components/forms/formField';
-import SelectField from 'app/views/settings/components/forms/selectField';
-
-import {DEFAULT_AGGREGATE} from './constants';
-import MetricField from './metricField';
-import {Datasource, IncidentRule, TimeWindow} from './types';
-
-const TIME_WINDOW_MAP: Record<TimeWindow, string> = {
-  [TimeWindow.ONE_MINUTE]: t('1 minute window'),
-  [TimeWindow.FIVE_MINUTES]: t('5 minute window'),
-  [TimeWindow.TEN_MINUTES]: t('10 minute window'),
-  [TimeWindow.FIFTEEN_MINUTES]: t('15 minute window'),
-  [TimeWindow.THIRTY_MINUTES]: t('30 minute window'),
-  [TimeWindow.ONE_HOUR]: t('1 hour window'),
-  [TimeWindow.TWO_HOURS]: t('2 hour window'),
-  [TimeWindow.FOUR_HOURS]: t('4 hour window'),
-  [TimeWindow.ONE_DAY]: t('24 hour window'),
-};
-
-type Props = {
-  api: Client;
-  organization: Organization;
-  projectSlug: string;
-  disabled: boolean;
-  thresholdChart: React.ReactElement;
-  onFilterSearch: (query: string) => void;
-};
-
-type State = {
-  environments: Environment[] | null;
-};
-
-class RuleConditionsFormWithGuiFilters extends React.PureComponent<Props, State> {
-  state: State = {
-    environments: null,
-  };
-
-  componentDidMount() {
-    this.fetchData();
-  }
-
-  async fetchData() {
-    const {api, organization, projectSlug} = this.props;
-
-    try {
-      const environments = await api.requestPromise(
-        `/projects/${organization.slug}/${projectSlug}/environments/`,
-        {
-          query: {
-            visibility: 'visible',
-          },
-        }
-      );
-      this.setState({environments});
-    } catch (_err) {
-      addErrorMessage(t('Unable to fetch environments'));
-    }
-  }
-
-  render() {
-    const {organization, disabled, onFilterSearch} = this.props;
-    const {environments} = this.state;
-
-    const environmentList: [IncidentRule['environment'], React.ReactNode][] =
-      environments?.map((env: Environment) => [env.name, getDisplayName(env)]) ?? [];
-
-    const anyEnvironmentLabel = (
-      <React.Fragment>
-        {t('All')}
-        <div className="all-environment-note">
-          {tct(
-            `This will count events across every environment. For example,
-             having 50 [code1:production] events and 50 [code2:development]
-             events would trigger an alert with a critical threshold of 100.`,
-            {code1: <code />, code2: <code />}
-          )}
-        </div>
-      </React.Fragment>
-    );
-    environmentList.unshift([null, anyEnvironmentLabel]);
-
-    const formElemBaseStyle = {
-      padding: `${space(0.5)}`,
-      border: 'none',
-    };
-
-    return (
-      <Panel>
-        <StyledPanelBody>
-          <StyledList symbol="colored-numeric">
-            <ListItem>{t('Select events')}</ListItem>
-            <FormRow>
-              <SelectField
-                name="environment"
-                placeholder={t('All')}
-                style={{
-                  ...formElemBaseStyle,
-                  minWidth: 250,
-                  flex: 1,
-                }}
-                styles={{
-                  singleValue: (base: any) => ({
-                    ...base,
-                    '.all-environment-note': {display: 'none'},
-                  }),
-                  option: (base: any, state: any) => ({
-                    ...base,
-                    '.all-environment-note': {
-                      ...(!state.isSelected && !state.isFocused
-                        ? {color: theme.gray400}
-                        : {}),
-                      fontSize: theme.fontSizeSmall,
-                    },
-                  }),
-                }}
-                choices={environmentList}
-                isDisabled={disabled || this.state.environments === null}
-                isClearable
-                inline={false}
-                flexibleControlStateSize
-                inFieldLabel={t('Environment: ')}
-              />
-              <Feature requireAll features={['organizations:performance-view']}>
-                <FormField
-                  name="datasource"
-                  inline={false}
-                  style={{
-                    ...formElemBaseStyle,
-                    minWidth: 300,
-                    flex: 2,
-                  }}
-                  flexibleControlStateSize
-                >
-                  {({onChange, onBlur, model}) => {
-                    const formDataset = model.getValue('dataset');
-                    const formEventTypes = model.getValue('eventTypes');
-                    const mappedValue = convertDatasetEventTypesToSource(
-                      formDataset,
-                      formEventTypes
-                    );
-                    return (
-                      <SelectControl
-                        value={mappedValue}
-                        inFieldLabel={t('Data Source: ')}
-                        onChange={optionObj => {
-                          const optionValue = optionObj.value;
-                          onChange(optionValue, {});
-                          onBlur(optionValue, {});
-                          // Reset the aggregate to the default (which works across
-                          // datatypes), otherwise we may send snuba an invalid query
-                          // (transaction aggregate on events datasource = bad).
-                          model.setValue('aggregate', DEFAULT_AGGREGATE);
-
-                          // set the value of the dataset and event type from data source
-                          const {dataset, eventTypes} =
-                            DATA_SOURCE_TO_SET_AND_EVENT_TYPES[optionValue] ?? {};
-                          model.setValue('dataset', dataset);
-                          model.setValue('eventTypes', eventTypes);
-                        }}
-                        options={[
-                          {
-                            label: t('Errors'),
-                            options: [
-                              {
-                                value: Datasource.ERROR_DEFAULT,
-                                label: DATA_SOURCE_LABELS[Datasource.ERROR_DEFAULT],
-                              },
-                              {
-                                value: Datasource.DEFAULT,
-                                label: DATA_SOURCE_LABELS[Datasource.DEFAULT],
-                              },
-                              {
-                                value: Datasource.ERROR,
-                                label: DATA_SOURCE_LABELS[Datasource.ERROR],
-                              },
-                            ],
-                          },
-                          {
-                            label: t('Transactions'),
-                            options: [
-                              {
-                                value: Datasource.TRANSACTION,
-                                label: DATA_SOURCE_LABELS[Datasource.TRANSACTION],
-                              },
-                            ],
-                          },
-                        ]}
-                        isDisabled={disabled}
-                        required
-                      />
-                    );
-                  }}
-                </FormField>
-              </Feature>
-              <FormField
-                name="query"
-                inline={false}
-                style={{
-                  ...formElemBaseStyle,
-                  flex: '6 0 700px',
-                }}
-                flexibleControlStateSize
-              >
-                {({onChange, onBlur, onKeyDown, initialData, model}) => (
-                  <SearchContainer>
-                    <StyledSearchBar
-                      defaultQuery={initialData?.query ?? ''}
-                      omitTags={['event.type']}
-                      disabled={disabled}
-                      useFormWrapper={false}
-                      organization={organization}
-                      placeholder={
-                        model.getValue('dataset') === 'events'
-                          ? t('Filter events by level, message, or other properties...')
-                          : t('Filter transactions by URL, tags, and other properties...')
-                      }
-                      onChange={onChange}
-                      onKeyDown={e => {
-                        /**
-                         * Do not allow enter key to submit the alerts form since it is unlikely
-                         * users will be ready to create the rule as this sits above required fields.
-                         */
-                        if (e.key === 'Enter') {
-                          e.preventDefault();
-                          e.stopPropagation();
-                        }
-
-                        onKeyDown?.(e);
-                      }}
-                      onBlur={query => {
-                        onFilterSearch(query);
-                        onBlur(query);
-                      }}
-                      onSearch={query => {
-                        onFilterSearch(query);
-                        onChange(query, {});
-                      }}
-                    />
-                  </SearchContainer>
-                )}
-              </FormField>
-            </FormRow>
-            <ListItem>{t('Choose a metric')}</ListItem>
-            <FormRow>
-              <MetricField
-                name="aggregate"
-                help={null}
-                organization={organization}
-                disabled={disabled}
-                style={{
-                  ...formElemBaseStyle,
-                }}
-                inline={false}
-                flexibleControlStateSize
-                columnWidth={250}
-                inFieldLabels
-                required
-              />
-              <FormRowText>{t('over a')}</FormRowText>
-              <Tooltip
-                title={t('Triggers are evaluated every minute regardless of this value.')}
-              >
-                <SelectField
-                  name="timeWindow"
-                  style={{
-                    ...formElemBaseStyle,
-                    flex: 1,
-                    minWidth: 180,
-                  }}
-                  choices={Object.entries(TIME_WINDOW_MAP)}
-                  required
-                  isDisabled={disabled}
-                  getValue={value => Number(value)}
-                  setValue={value => `${value}`}
-                  inline={false}
-                  flexibleControlStateSize
-                />
-              </Tooltip>
-            </FormRow>
-          </StyledList>
-          {this.props.thresholdChart}
-        </StyledPanelBody>
-      </Panel>
-    );
-  }
-}
-
-const StyledPanelBody = styled(PanelBody)`
-  ol,
-  h4 {
-    margin-bottom: ${space(1)};
-  }
-`;
-
-const SearchContainer = styled('div')`
-  display: flex;
-`;
-
-const StyledSearchBar = styled(SearchBar)`
-  flex-grow: 1;
-`;
-
-const StyledList = styled(List)`
-  padding: ${space(3)} ${space(3)} 0 ${space(3)};
-`;
-
-const FormRow = styled('div')`
-  display: flex;
-  flex-direction: row;
-  align-items: flex-end;
-  flex-wrap: wrap;
-  margin-bottom: ${space(2)};
-`;
-
-const FormRowText = styled('div')`
-  padding: ${space(0.5)};
-  /* Match the height of the select controls */
-  line-height: 36px;
-`;
-
-export default RuleConditionsFormWithGuiFilters;

+ 63 - 81
src/sentry/static/sentry/app/views/settings/incidentRules/ruleForm/index.tsx

@@ -10,7 +10,6 @@ import {
 } from 'app/actionCreators/indicator';
 import {fetchOrganizationTags} from 'app/actionCreators/tags';
 import Access from 'app/components/acl/access';
-import Feature from 'app/components/acl/feature';
 import AsyncComponent from 'app/components/asyncComponent';
 import Button from 'app/components/button';
 import Confirm from 'app/components/confirm';
@@ -29,7 +28,6 @@ import hasThresholdValue from 'app/views/settings/incidentRules/utils/hasThresho
 import {addOrUpdateRule} from '../actions';
 import {createDefaultTrigger} from '../constants';
 import RuleConditionsForm from '../ruleConditionsForm';
-import RuleConditionsFormWithGuiFilters from '../ruleConditionsFormWithGuiFilters';
 import {
   AlertRuleThresholdType,
   Dataset,
@@ -565,86 +563,70 @@ class RuleFormContainer extends AsyncComponent<Props, State> {
     return (
       <Access access={['alerts:write']}>
         {({hasAccess}) => (
-          <Feature features={['metric-alert-gui-filters']} organization={organization}>
-            {({hasFeature}) => (
-              <Form
-                apiMethod={ruleId ? 'PUT' : 'POST'}
-                apiEndpoint={`/organizations/${organization.slug}/alert-rules/${
-                  ruleId ? `${ruleId}/` : ''
-                }`}
-                submitDisabled={!hasAccess}
-                initialData={{
-                  name: rule.name || '',
-                  dataset: rule.dataset,
-                  eventTypes: hasFeature ? rule.eventTypes : undefined,
-                  aggregate: rule.aggregate,
-                  query: rule.query || '',
-                  timeWindow: rule.timeWindow,
-                  environment: rule.environment || null,
-                }}
-                saveOnBlur={false}
-                onSubmit={this.handleSubmit}
-                onSubmitSuccess={onSubmitSuccess}
-                onCancel={this.handleCancel}
-                onFieldChange={this.handleFieldChange}
-                extraButton={
-                  !!rule.id ? (
-                    <Confirm
-                      disabled={!hasAccess}
-                      message={t('Are you sure you want to delete this alert rule?')}
-                      header={t('Delete Alert Rule?')}
-                      priority="danger"
-                      confirmText={t('Delete Rule')}
-                      onConfirm={this.handleDeleteRule}
-                    >
-                      <Button type="button" priority="danger">
-                        {t('Delete Rule')}
-                      </Button>
-                    </Confirm>
-                  ) : null
-                }
-                submitLabel={t('Save Rule')}
-              >
-                {hasFeature ? (
-                  <RuleConditionsFormWithGuiFilters
-                    api={this.api}
-                    projectSlug={params.projectId}
-                    organization={organization}
-                    disabled={!hasAccess}
-                    thresholdChart={chart}
-                    onFilterSearch={this.handleFilterUpdate}
-                  />
-                ) : (
-                  <RuleConditionsForm
-                    api={this.api}
-                    projectSlug={params.projectId}
-                    organization={organization}
-                    disabled={!hasAccess}
-                    thresholdChart={chart}
-                    onFilterSearch={this.handleFilterUpdate}
-                  />
-                )}
-
-                <Triggers
+          <Form
+            apiMethod={ruleId ? 'PUT' : 'POST'}
+            apiEndpoint={`/organizations/${organization.slug}/alert-rules/${
+              ruleId ? `${ruleId}/` : ''
+            }`}
+            submitDisabled={!hasAccess}
+            initialData={{
+              name: rule.name || '',
+              dataset: rule.dataset,
+              eventTypes: rule.eventTypes,
+              aggregate: rule.aggregate,
+              query: rule.query || '',
+              timeWindow: rule.timeWindow,
+              environment: rule.environment || null,
+            }}
+            saveOnBlur={false}
+            onSubmit={this.handleSubmit}
+            onSubmitSuccess={onSubmitSuccess}
+            onCancel={this.handleCancel}
+            onFieldChange={this.handleFieldChange}
+            extraButton={
+              !!rule.id ? (
+                <Confirm
                   disabled={!hasAccess}
-                  projects={this.state.projects}
-                  errors={this.state.triggerErrors}
-                  triggers={triggers}
-                  resolveThreshold={resolveThreshold}
-                  thresholdType={thresholdType}
-                  currentProject={params.projectId}
-                  organization={organization}
-                  ruleId={ruleId}
-                  availableActions={this.state.availableActions}
-                  onChange={this.handleChangeTriggers}
-                  onThresholdTypeChange={this.handleThresholdTypeChange}
-                  onResolveThresholdChange={this.handleResolveThresholdChange}
-                />
-
-                <RuleNameForm disabled={!hasAccess} />
-              </Form>
-            )}
-          </Feature>
+                  message={t('Are you sure you want to delete this alert rule?')}
+                  header={t('Delete Alert Rule?')}
+                  priority="danger"
+                  confirmText={t('Delete Rule')}
+                  onConfirm={this.handleDeleteRule}
+                >
+                  <Button type="button" priority="danger">
+                    {t('Delete Rule')}
+                  </Button>
+                </Confirm>
+              ) : null
+            }
+            submitLabel={t('Save Rule')}
+          >
+            <RuleConditionsForm
+              api={this.api}
+              projectSlug={params.projectId}
+              organization={organization}
+              disabled={!hasAccess}
+              thresholdChart={chart}
+              onFilterSearch={this.handleFilterUpdate}
+            />
+            <Triggers
+              disabled={!hasAccess}
+              projects={this.state.projects}
+              errors={this.state.triggerErrors}
+              triggers={triggers}
+              resolveThreshold={resolveThreshold}
+              thresholdType={thresholdType}
+              currentProject={params.projectId}
+              organization={organization}
+              ruleId={ruleId}
+              availableActions={this.state.availableActions}
+              onChange={this.handleChangeTriggers}
+              onThresholdTypeChange={this.handleThresholdTypeChange}
+              onResolveThresholdChange={this.handleResolveThresholdChange}
+            />
+
+            <RuleNameForm disabled={!hasAccess} />
+          </Form>
         )}
       </Access>
     );

+ 1 - 37
src/sentry/static/sentry/app/views/settings/incidentRules/triggers/chart/index.tsx

@@ -15,7 +15,6 @@ import {
 } from 'app/components/charts/styles';
 import SelectControl from 'app/components/forms/selectControl';
 import LoadingMask from 'app/components/loadingMask';
-import {Panel, PanelBody} from 'app/components/panels';
 import Placeholder from 'app/components/placeholder';
 import {t} from 'app/locale';
 import space from 'app/styles/space';
@@ -305,24 +304,7 @@ class TriggersChart extends React.PureComponent<Props, State> {
                   </React.Fragment>
                 );
 
-                return (
-                  <Feature
-                    organization={organization}
-                    features={['metric-alert-gui-filters']}
-                  >
-                    {({hasFeature: hasGuiFilters}) =>
-                      hasGuiFilters ? (
-                        <React.Fragment>{chart}</React.Fragment>
-                      ) : (
-                        <StickyWrapper>
-                          <StyledPanel>
-                            <StyledPanelBody>{chart}</StyledPanelBody>
-                          </StyledPanel>
-                        </StickyWrapper>
-                      )
-                    }
-                  </Feature>
-                );
+                return chart;
               }}
             </EventsRequest>
           );
@@ -346,24 +328,6 @@ const ChartPlaceholder = styled(Placeholder)`
   height: 184px;
 `;
 
-const StickyWrapper = styled('div')`
-  position: sticky;
-  top: ${space(1)};
-  z-index: ${p => p.theme.zIndex.dropdown - 1};
-`;
-
-const StyledPanel = styled(Panel)`
-  /* Remove margin for the sticky window */
-  margin-bottom: 0;
-`;
-
-const StyledPanelBody = styled(PanelBody)`
-  h4 {
-    margin-bottom: ${space(1)};
-  }
-  padding-top: ${space(2)};
-`;
-
 const PeriodSelectControl = styled(SelectControl)`
   display: inline-block;
   width: 180px;